Commit Graph
11962 Commits
Author SHA1 Message Date
chrislu cd9b39ca50 feat: automatic idle partition cleanup to prevent memory bloat
Implements automatic cleanup of topic partitions with no active publishers
or subscribers to prevent memory accumulation from short-lived topics.

**Key Features:**

1. Activity Tracking (local_partition.go)
   - Added lastActivityTime field to LocalPartition
   - UpdateActivity() called on publish, subscribe, and message reads
   - IsIdle() checks if partition has no publishers/subscribers
   - GetIdleDuration() returns time since last activity
   - ShouldCleanup() determines if partition eligible for cleanup

2. Cleanup Task (local_manager.go)
   - Background goroutine runs every 1 minute (configurable)
   - Removes partitions idle for > 5 minutes (configurable)
   - Automatically removes empty topics after all partitions cleaned
   - Proper shutdown handling with WaitForCleanupShutdown()

3. Broker Integration (broker_server.go)
   - StartIdlePartitionCleanup() called on broker startup
   - Default: check every 1 minute, cleanup after 5 minutes idle
   - Transparent operation with sensible defaults

**Cleanup Process:**
- Checks: partition.Publishers.Size() == 0 && partition.Subscribers.Size() == 0
- Calls partition.Shutdown() to:
  - Flush all data to disk (no data loss)
  - Stop 3 goroutines (loopFlush, loopInterval, cleanupLoop)
  - Free in-memory buffers (~100KB-10MB per partition)
  - Close LogBuffer resources
- Removes partition from LocalTopic.Partitions
- Removes topic if no partitions remain

**Benefits:**
- Prevents memory bloat from short-lived topics
- Reduces goroutine count (3 per partition cleaned)
- Zero configuration required
- Data remains on disk, can be recreated on demand
- No impact on active partitions

**Example Logs:**
  I Started idle partition cleanup task (check: 1m, timeout: 5m)
  I Cleaning up idle partition topic-0 (idle for 5m12s, publishers=0, subscribers=0)
  I Cleaned up 2 idle partition(s)

**Memory Freed per Partition:**
- In-memory message buffer: ~100KB-10MB
- Disk buffer cache
- 3 goroutines
- Publisher/subscriber tracking maps
- Condition variables and mutexes

**Related Issue:**
Prevents memory accumulation in systems with high topic churn or
many short-lived consumer groups, improving long-term stability
and resource efficiency.

**Testing:**
- Compiles cleanly
- No linting errors
- Ready for integration testing

fmt
2025-10-15 22:41:06 -07:00
chrislu 7e46abf052 fmt 2025-10-15 22:00:38 -07:00
chrislu 2ffdda2661 fix: commit offsets in Cleanup() before rebalancing
This commit adds explicit offset commit in the ConsumerGroupHandler.Cleanup()
method, which is called during consumer group rebalancing. This ensures all
marked offsets are committed BEFORE partitions are reassigned to other consumers,
significantly reducing duplicate message consumption during rebalancing.

Problem:
- Cleanup() was not committing offsets before rebalancing
- When partition reassigned to another consumer, it started from last committed offset
- Uncommitted messages (processed but not yet committed) were read again by new consumer
- This caused ~100-200% duplicate messages during rebalancing in tests

Solution:
- Add session.Commit() in Cleanup() method
- This runs after all ConsumeClaim goroutines have exited
- Ensures all MarkMessage() calls are committed before partition release
- New consumer starts from the last processed offset, not an older committed offset

Benefits:
- Dramatically reduces duplicate messages during rebalancing
- Improves at-least-once semantics (closer to exactly-once for normal cases)
- Better performance (less redundant processing)
- Cleaner test results (expected duplicates only from actual failures)

Kafka Rebalancing Lifecycle:
1. Rebalance triggered (consumer join/leave, timeout, etc.)
2. All ConsumeClaim goroutines cancelled
3. Cleanup() called ← WE COMMIT HERE NOW
4. Partitions reassigned to other consumers
5. New consumer starts from last committed offset ← NOW MORE UP-TO-DATE

Expected Results:
- Before: ~100-200% duplicates during rebalancing (2-3x reads)
- After: <10% duplicates (only from uncommitted in-flight messages)

This is a critical fix for production deployments where consumer churn
(scaling, restarts, failures) causes frequent rebalancing.
2025-10-15 22:00:32 -07:00
chrislu 7e755c70ce feat: add in-memory cache for disk chunk reads
This commit adds an LRU cache for disk chunks to optimize repeated reads
of historical data. When multiple consumers read the same historical offsets,
or a single consumer refetches the same data, the cache eliminates redundant
disk I/O.

Cache Design:
- Chunk size: 1000 messages per chunk
- Max chunks: 16 (configurable, ~16K messages cached)
- Eviction policy: LRU (Least Recently Used)
- Thread-safe with RWMutex
- Chunk-aligned offsets for efficient lookups

New Components:
1. DiskChunkCache struct - manages cached chunks
2. CachedDiskChunk struct - stores chunk data with metadata
3. getCachedDiskChunk() - checks cache before disk read
4. cacheDiskChunk() - stores chunks with LRU eviction
5. extractMessagesFromCache() - extracts subset from cached chunk

How It Works:
1. Read request for offset N (e.g., 2500)
2. Calculate chunk start: (2500 / 1000) * 1000 = 2000
3. Check cache for chunk starting at 2000
4. If HIT: Extract messages 2500-2999 from cached chunk
5. If MISS: Read chunk 2000-2999 from disk, cache it, extract 2500-2999
6. If cache full: Evict LRU chunk before caching new one

Benefits:
- Eliminates redundant disk I/O for popular historical data
- Reduces latency for repeated reads (cache hit ~1ms vs disk ~100ms)
- Supports multiple consumers reading same historical offsets
- Automatically evicts old chunks when cache is full
- Zero impact on hot path (in-memory reads unchanged)

Performance Impact:
- Cache HIT: ~99% faster than disk read
- Cache MISS: Same as disk read (with caching overhead ~1%)
- Memory: ~16MB for 16 chunks (16K messages x 1KB avg)

Example Scenario (CI tests):
- Producer writes offsets 0-4
- Data flushes to disk
- Consumer 1 reads 0-4 (cache MISS, reads from disk, caches chunk 0-999)
- Consumer 2 reads 0-4 (cache HIT, served from memory)
- Consumer 1 rebalances, re-reads 0-4 (cache HIT, no disk I/O)

This optimization is especially valuable in CI environments where:
- Small memory buffers cause frequent flushing
- Multiple consumers read the same historical data
- Disk I/O is relatively slow compared to memory access
2025-10-15 21:40:36 -07:00
chrislu 0e481cf97a fmt 2025-10-15 21:37:07 -07:00
chrislu c5634470ed feat: add disk I/O fallback for historical offset reads
This commit implements async disk I/O fallback to handle cases where:
1. Data is flushed from memory before consumers can read it (CI issue)
2. Consumers request historical offsets not in memory
3. Small LogBuffer retention in resource-constrained environments

Changes:
- Add readHistoricalDataFromDisk() helper function
- Update ReadMessagesAtOffset() to call ReadFromDiskFn when offset < bufferStartOffset
- Properly handle maxMessages and maxBytes limits during disk reads
- Return appropriate nextOffset after disk reads
- Log disk read operations at V(2) and V(3) levels

Benefits:
- Fixes CI test failures where data is flushed before consumption
- Enables consumers to catch up even if they fall behind memory retention
- No blocking on hot path (disk read only for historical data)
- Respects existing ReadFromDiskFn timeout handling

How it works:
1. Try in-memory read first (fast path)
2. If offset too old and ReadFromDiskFn configured, read from disk
3. Return disk data with proper nextOffset
4. Consumer continues reading seamlessly

This fixes the 'offset 0 too old (earliest in-memory: 5)' error in
TestOffsetManagement where messages were flushed before consumer started.
2025-10-15 21:35:39 -07:00
chrislu e1a4bff794 feat: add context timeout propagation to produce path
This commit adds proper context propagation throughout the produce path,
enabling client-side timeouts to be honored on the broker side. Previously,
only fetch operations respected client timeouts - produce operations continued
indefinitely even if the client gave up.

Changes:
- Add ctx parameter to ProduceRecord and ProduceRecordValue signatures
- Add ctx parameter to PublishRecord and PublishRecordValue in BrokerClient
- Add ctx parameter to handleProduce and related internal functions
- Update all callers (protocol handlers, mocks, tests) to pass context
- Add context cancellation checks in PublishRecord before operations

Benefits:
- Faster failure detection when client times out
- No orphaned publish operations consuming broker resources
- Resource efficiency improvements (no goroutine/stream/lock leaks)
- Consistent timeout behavior between produce and fetch paths
- Better error handling with proper cancellation signals

This fixes the root cause of CI test timeouts where produce operations
continued indefinitely after clients gave up, leading to cascading delays.
2025-10-15 20:31:44 -07:00
chrislu 66d87659e5 test: increase timeouts for consumer group operations in E2E tests
Consumer group operations (coordinator discovery, offset fetch/commit) are
slower in CI environments with limited resources. This increases timeouts to:
- ProduceMessages: 10s -> 30s (for when consumer groups are active)
- ConsumeWithGroup: 30s -> 60s (for offset fetch/commit operations)

Fixes the TestOffsetManagement timeout failures in GitHub Actions CI.
2025-10-15 20:15:46 -07:00
chrislu 39e7bbdc6d less logs 2025-10-15 20:01:28 -07:00
chrislu a12f7b2ee8 fix go mod 2025-10-15 19:51:40 -07:00
chrislu 53f9124a26 fix tests 2025-10-15 19:00:10 -07:00
chrislu 1807b8093c debug fetch offset APIs 2025-10-15 18:59:46 -07:00
chrislu ba1a8aed64 log read stateless 2025-10-15 16:08:21 -07:00
chrislu 210fc49891 Merge branch 'master' into fix-race-condition 2025-10-15 11:28:32 -07:00
Chris LuandGitHub 3d25f206c8 S3: Signature verification should not check permissions (#7335)
* Signature verification should not check permissions - that's done later in authRequest

* test permissions during signature verfication

* fix s3 test path

* s3tests_boto3 => s3tests

* remove extra lines
2025-10-15 11:27:39 -07:00
chrislu 3b75e50b04 removing the unnecessary restart logic and relying on the seek mechanism we already implemented 2025-10-15 10:50:54 -07:00
chrislu 6c1298b5f7 track messages with testStartTime 2025-10-15 10:22:36 -07:00
chrislu f4a018e731 verify produced messages are consumed 2025-10-15 09:58:31 -07:00
chrislu e7747a7572 adjust s3 tests 2025-10-15 09:57:23 -07:00
chrislu 38befd30ee pin s3 test version 2025-10-15 09:41:45 -07:00
chrislu 0bf4ace6b1 reuse cached records 2025-10-15 09:27:12 -07:00
chrislu 7e934d6283 ack messages to broker 2025-10-15 09:12:03 -07:00
chrislu 5222ddaf2f seekable subscribe messages 2025-10-15 00:40:27 -07:00
chrislu 60e6e63706 avoid goroutine leak 2025-10-14 23:49:10 -07:00
chrislu f639c42472 clean up consumer protocols 2025-10-14 19:01:12 -07:00
chrislu e344c6ce24 adjust return values on failures 2025-10-14 18:38:14 -07:00
chrislu 0cbc5e906e purge unused 2025-10-14 18:22:09 -07:00
chrislu fd33e03008 less logs, remove unused code 2025-10-14 17:28:54 -07:00
chrislu bb0e613275 more time 2025-10-14 16:58:50 -07:00
chrislu 5c6b0eaa0d Update fetch.go 2025-10-14 16:44:55 -07:00
chrislu 718113d085 adjust deadline 2025-10-14 16:31:11 -07:00
chrislu e9101d9733 add some delays 2025-10-14 16:15:28 -07:00
chrislu 090f73dc66 less logs 2025-10-14 16:01:12 -07:00
chrislu 7c0c212d33 use client timeout wait 2025-10-14 15:40:01 -07:00
chrislu 4766534b84 increase deadline 2025-10-14 14:17:57 -07:00
chrislu 54f4a4285a consumer group that does not join group 2025-10-14 13:16:01 -07:00
chrislu 9e78705a98 refactor dedup 2025-10-14 13:05:08 -07:00
chrislu 2a0b7604c5 avoid race condition 2025-10-14 13:04:33 -07:00
chrislu 1f128d65c5 debug 2025-10-14 12:32:31 -07:00
chrislu 9eae9e1fed unlock 2025-10-14 11:02:42 -07:00
chrislu 98b536480d fix locking 2025-10-14 09:47:15 -07:00
chrislu 73ebc69a82 avoid deadlock 2025-10-14 07:51:06 -07:00
chrislu fe9e0161d5 fmt 2025-10-14 07:47:18 -07:00
chrislu 92a7e42368 atomic currentStartOffset 2025-10-14 00:54:15 -07:00
chrislu e2c6f47cf6 Simplified GetOrCreateSubscriber to always reuse existing sessions 2025-10-14 00:41:02 -07:00
chrislu 6ef2f66198 only recreate if we need to seek backward (requested offset < current offset), not on any mismatch 2025-10-14 00:26:55 -07:00
chrislu 6947d906a8 more logs on offset resume 2025-10-13 23:52:24 -07:00
chrislu 63b3a10535 comment 2025-10-13 23:49:52 -07:00
chrislu bc7e015a41 Inlined the session creation logic to hold the lock continuously 2025-10-13 23:18:13 -07:00
chrislu 2ff548a41d save checkpoint every 2 seconds 2025-10-13 23:03:16 -07:00