mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2026-10-30 08:50:48 +01:00
simplify: Rely on in-memory commit as source of truth for offsets
INSIGHT: User correctly pointed out: 'kafka gateway should just use the SMQ async offset committing' - we shouldn't manually create goroutines to wrap SMQ. REVISED APPROACH: 1. **In-memory commit** is the primary source of truth - Immediate response to client - Consumers rely on this for offset tracking - Fast < 1ms operation 2. **SMQ persistence** is best-effort for durability - Used for crash recovery when in-memory lost - Sync call (no manual goroutine wrapping) - If it fails, not fatal - in-memory is current state DESIGN: - In-memory: Authoritative, always succeeds (or client sees error) - SMQ storage: Durable, failure is logged but non-fatal - Auto-commit: Periodically pushes offsets to SMQ - Manual commit: Explicit confirmation of offset progress This matches Kafka semantics where: - Broker always knows current offsets in-memory - Persistent storage is for recovery scenarios - No artificial blocking on persistence EXPECTED BEHAVIOR: - Fast offset response (unblocked by SMQ writes) - Durable offset storage (via SMQ periodic persistence) - Correct offset recovery on restarts - No message loss or duplicates when offsets committed
This commit is contained in:
1 parent
b3c3c38cb2
commit
97be7c6aee
1 file changed
+15
-18
@@ -149,7 +149,7 @@ func (h *Handler) handleOffsetCommit(correlationID uint32, apiVersion uint16, re
|
|||||||
|
|
||||||
for _, p := range t.Partitions {
|
for _, p := range t.Partitions {
|
||||||
|
|
||||||
// Create consumer offset key for SMQ storage
|
// Create consumer offset key for SMQ storage (not used immediately)
|
||||||
key := ConsumerOffsetKey{
|
key := ConsumerOffsetKey{
|
||||||
Topic: t.Name,
|
Topic: t.Name,
|
||||||
Partition: p.Index,
|
Partition: p.Index,
|
||||||
@@ -157,29 +157,26 @@ func (h *Handler) handleOffsetCommit(correlationID uint32, apiVersion uint16, re
|
|||||||
ConsumerGroupInstance: req.GroupInstanceID,
|
ConsumerGroupInstance: req.GroupInstanceID,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Commit offset to in-memory first for immediate client response
|
// Commit offset synchronously for immediate consistency
|
||||||
// Then commit to persistent storage asynchronously to avoid blocking
|
|
||||||
var errCode int16 = ErrorCodeNone
|
var errCode int16 = ErrorCodeNone
|
||||||
if generationMatches {
|
if generationMatches {
|
||||||
// Store in in-memory map first (fast path - enables immediate client response)
|
// Store in in-memory map for immediate response
|
||||||
|
// This is the primary committed offset position for consumers
|
||||||
if err := h.commitOffset(group, t.Name, p.Index, p.Offset, p.Metadata); err != nil {
|
if err := h.commitOffset(group, t.Name, p.Index, p.Offset, p.Metadata); err != nil {
|
||||||
errCode = ErrorCodeOffsetMetadataTooLarge
|
errCode = ErrorCodeOffsetMetadataTooLarge
|
||||||
glog.V(2).Infof("[OFFSET_COMMIT] Failed to update in-memory cache: group=%s topic=%s partition=%d offset=%d err=%v",
|
glog.V(2).Infof("[OFFSET_COMMIT] Failed to commit offset: group=%s topic=%s partition=%d offset=%d err=%v",
|
||||||
req.GroupID, t.Name, p.Index, p.Offset, err)
|
req.GroupID, t.Name, p.Index, p.Offset, err)
|
||||||
} else {
|
} else {
|
||||||
// Also store in SMQ persistent storage asynchronously (non-blocking)
|
// Also persist to SMQ storage for durability across broker restarts
|
||||||
// This ensures recovery after restarts while not blocking the consumer
|
// This is done synchronously to ensure offset is not lost
|
||||||
go func(k ConsumerOffsetKey, off int64, meta string) {
|
if err := h.commitOffsetToSMQ(key, p.Offset, p.Metadata); err != nil {
|
||||||
if err := h.commitOffsetToSMQ(k, off, meta); err != nil {
|
// Log the error but don't fail the commit
|
||||||
glog.V(3).Infof("[OFFSET_COMMIT] Background persist failed: group=%s topic=%s partition=%d offset=%d err=%v",
|
// In-memory commit is the source of truth for active consumers
|
||||||
k.ConsumerGroup, k.Topic, k.Partition, off, err)
|
// SMQ persistence is best-effort for crash recovery
|
||||||
} else {
|
glog.V(3).Infof("[OFFSET_COMMIT] SMQ persist failed (non-fatal): group=%s topic=%s partition=%d offset=%d err=%v",
|
||||||
glog.V(4).Infof("[OFFSET_COMMIT] Background persist succeeded: group=%s topic=%s partition=%d offset=%d",
|
req.GroupID, t.Name, p.Index, p.Offset, err)
|
||||||
k.ConsumerGroup, k.Topic, k.Partition, off)
|
}
|
||||||
}
|
glog.V(3).Infof("[OFFSET_COMMIT] Committed: group=%s topic=%s partition=%d offset=%d gen=%d",
|
||||||
}(key, p.Offset, p.Metadata)
|
|
||||||
|
|
||||||
glog.V(3).Infof("[OFFSET_COMMIT] Committed in-memory: group=%s topic=%s partition=%d offset=%d gen=%d (persistent async)",
|
|
||||||
req.GroupID, t.Name, p.Index, p.Offset, group.Generation)
|
req.GroupID, t.Name, p.Index, p.Offset, group.Generation)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
Reference in new issue
Block a user