This fixes the root cause of message loss: offset resets to auto.offset.reset. ROOT CAUSE: When OffsetFetch is called during rebalancing: 1. Offset not found in memory → returns -1 2. Consumer gets -1 → triggers auto.offset.reset=earliest 3. Consumer restarts from offset 0 4. Previously consumed messages 39-786 are never fetched again ANALYSIS: Test shows missing messages are contiguous ranges: - loadtest-topic-2[0]: Missing offsets 39-786 (748 messages) - loadtest-topic-0[1]: Missing 675 messages from offset ~117 - Pattern: Initial messages 0-38 consumed, then restart, then 39+ never fetched FIX: When OffsetFetch finds offset in SMQ storage: 1. Return the offset to client 2. IMMEDIATELY cache in in-memory map via h.commitOffset() 3. Next fetch will find it in memory (no reset) 4. Consumer continues from correct offset This prevents the offset reset loop that causes the 21% message loss. Revert "fix: Load persisted offsets into memory cache immediately on fetch" This reverts commit d9809eabb9206759b9eb4ffb8bf98b4c5c2f4c64. fix: Increase fetch timeout and add logging for timeout failures ROOT CAUSE: Consumer fetches messages 0-30 successfully, then ALL subsequent fetches fail silently. Partition reader stops responding after ~3-4 batches. ANALYSIS: The fetch request timeout is set to client's MaxWaitTime (100ms-500ms). When GetStoredRecords takes longer than this (disk I/O, broker latency), context times out. The multi-batch fetcher returns error/empty, fallback single-batch also times out, and function returns empty bytes silently. Consumer never retries - it just gets empty response and gives up. Result: Messages from offset 31+ are never fetched (3,956 missing = 32%). FIX: 1. Increase internal timeout to 1.5x client timeout (min 5 seconds) This allows batch fetchers to complete even if slightly delayed 2. Add comprehensive logging at WARNING level for timeout failures So we can diagnose these issues in the field 3. Better error messages with duration info Helps distinguish between timeout vs no-data situations This ensures the fetch path doesn't silently fail just because a batch took slightly longer than expected to fetch from disk. fix: Use fresh context for fallback fetch to avoid cascading timeouts PROBLEM IDENTIFIED: After previous fix, missing messages reduced 32%→16% BUT duplicates increased 18.5%→56.6%. Root cause: When multi-batch fetch times out, the fallback single-batch ALSO uses the expired context. Result: 1. Multi-batch fetch times out (context expired) 2. Fallback single-batch uses SAME expired context → also times out 3. Both return empty bytes 4. Consumer gets empty response, offset resets to memory cache 5. Consumer re-fetches from earlier offset 6. DUPLICATES result from re-fetching old messages FIX: Use ORIGINAL context for fallback fetch, not the timed-out fetchCtx. This gives the fallback a fresh chance to fetch data even if multi-batch timed out. IMPROVEMENTS: 1. Fallback now uses fresh context (not expired from multi-batch) 2. Add WARNING logs for ALL multi-batch failures (not just errors) 3. Distinguish between 'failed' (timed out) and 'no data available' 4. Log total duration for diagnostics Expected Result: - Duplicates should decrease significantly (56.6% → 5-10%) - Missing messages should stay low (~16%) or improve further - Warnings in logs will show which fetches are timing out fmt
SeaweedMQ Message Queue on SeaweedFS (WIP, not ready)
What are the use cases it is designed for?
Message queues are like water pipes. Messages flow in the pipes to their destinations.
However, what if a flood comes? Of course, you can increase the number of partitions, add more brokers, restart, and watch the traffic level closely.
Sometimes the flood is expected. For example, backfill some old data in batch, and switch to online messages. You may want to ensure enough brokers to handle the data and reduce them later to cut cost.
SeaweedMQ is designed for use cases that need to:
- Receive and save large number of messages.
- Handle spike traffic automatically.
What is special about SeaweedMQ?
- Separate computation and storage nodes to scale independently.
- Unlimited storage space by adding volume servers.
- Unlimited message brokers to handle incoming messages.
- Offline messages can be operated as normal files.
- Scale up and down with auto split and merge message topics.
- Topics can automatically split into segments when traffic increases, and vice verse.
- Pass messages by reference instead of copying.
- Clients can optionally upload the messages first and just submit the references.
- Drastically reduce the broker load.
- Stateless brokers
- All brokers are equal. One broker is dynamically picked as the leader.
- Add brokers at any time.
- Allow rolling restart brokers or remove brokers at a pace.
Design
How it works?
Brokers are just computation nodes without storage. When a broker starts, it reports itself to masters. Among all the brokers, one of them will be selected as the leader by the masters.
A topic needs to define its partition key on its messages.
Messages for a topic are divided into segments. One segment can cover a range of partitions. A segment can be split into 2 segments, or 2 neighboring segments can be merged back to one segment.
During write time, the client will ask the broker leader for a few brokers to process the segment.
The broker leader will check whether the segment already has assigned the brokers. If not, select a few brokers based on their loads, save the selection into filer, and tell the client.
The client will write the messages for this segment to the selected brokers.
Failover
The broker leader does not contain any state. If it fails, the masters will select a different broker.
For a segment, if any one of the selected brokers is down, the remaining brokers should try to write received messages to the filer, and close the segment to the clients.
Then the clients should start a new segment. The masters should assign other healthy brokers to handle the new segment.
So any brokers can go down without losing data.
Auto Split or Merge
(The idea is learned from Pravega.)
The brokers should report its traffic load to the broker leader periodically.
If any segment has too much load, the broker leader will ask the brokers to tell the client to close current one and create two new segments.
If 2 neighboring segments have the combined load below average load per segment, the broker leader will ask the brokers to tell the client to close this 2 segments and create a new segment.