Files
seaweedfs/weed/mq/sub_coordinator/inflight_message_tracker.go

161 lines
4.4 KiB
Go
Raw Normal View History

2024-05-20 09:19:39 -07:00
package sub_coordinator
import (
"sort"
"sync"
)
type InflightMessageTracker struct {
2024-05-20 11:05:18 -07:00
messages map[string]int64
mu sync.Mutex
timestamps *RingBuffer
2024-05-20 09:19:39 -07:00
}
func NewInflightMessageTracker(capacity int) *InflightMessageTracker {
return &InflightMessageTracker{
2024-05-20 11:05:18 -07:00
messages: make(map[string]int64),
timestamps: NewRingBuffer(capacity),
2024-05-20 09:19:39 -07:00
}
}
2024-05-29 23:33:37 -07:00
// EnflightMessage tracks the message with the key and timestamp.
2024-05-20 09:19:39 -07:00
// These messages are sent to the consumer group instances and waiting for ack.
2024-05-29 23:33:37 -07:00
func (imt *InflightMessageTracker) EnflightMessage(key []byte, tsNs int64) {
2024-05-30 09:49:08 -07:00
// fmt.Printf("EnflightMessage(%s,%d)\n", string(key), tsNs)
2024-05-20 09:19:39 -07:00
imt.mu.Lock()
defer imt.mu.Unlock()
imt.messages[string(key)] = tsNs
2024-05-29 23:33:37 -07:00
imt.timestamps.EnflightTimestamp(tsNs)
2024-05-20 09:19:39 -07:00
}
2024-05-20 11:05:18 -07:00
2024-05-20 09:19:39 -07:00
// IsMessageAcknowledged returns true if the message has been acknowledged.
// If the message is older than the oldest inflight messages, returns false.
// returns false if the message is inflight.
// Otherwise, returns false if the message is old and can be ignored.
func (imt *InflightMessageTracker) IsMessageAcknowledged(key []byte, tsNs int64) bool {
imt.mu.Lock()
defer imt.mu.Unlock()
2024-05-29 23:33:37 -07:00
if tsNs <= imt.timestamps.OldestAckedTimestamp() {
2024-05-20 09:19:39 -07:00
return true
}
if tsNs > imt.timestamps.Latest() {
return false
}
if _, found := imt.messages[string(key)]; found {
return false
}
return true
}
2024-05-20 11:05:18 -07:00
2024-05-20 09:19:39 -07:00
// AcknowledgeMessage acknowledges the message with the key and timestamp.
func (imt *InflightMessageTracker) AcknowledgeMessage(key []byte, tsNs int64) bool {
2024-05-30 09:49:08 -07:00
// fmt.Printf("AcknowledgeMessage(%s,%d)\n", string(key), tsNs)
2024-05-20 09:19:39 -07:00
imt.mu.Lock()
defer imt.mu.Unlock()
timestamp, exists := imt.messages[string(key)]
if !exists || timestamp != tsNs {
return false
}
delete(imt.messages, string(key))
// Remove the specific timestamp from the ring buffer.
2024-05-29 23:33:37 -07:00
imt.timestamps.AckTimestamp(tsNs)
2024-05-20 09:19:39 -07:00
return true
}
2024-05-29 23:33:37 -07:00
func (imt *InflightMessageTracker) GetOldestAckedTimestamp() int64 {
return imt.timestamps.OldestAckedTimestamp()
2024-05-20 09:33:37 -07:00
}
// IsInflight returns true if the message with the key is inflight.
func (imt *InflightMessageTracker) IsInflight(key []byte) bool {
imt.mu.Lock()
defer imt.mu.Unlock()
_, found := imt.messages[string(key)]
return found
}
2024-05-29 23:33:37 -07:00
type TimestampStatus struct {
Timestamp int64
Acked bool
}
2024-05-20 09:19:39 -07:00
// RingBuffer represents a circular buffer to hold timestamps.
type RingBuffer struct {
buffer []*TimestampStatus
head int
size int
2024-05-30 09:10:30 -07:00
maxTimestamp int64
maxAllAckedTs int64
2024-05-20 09:19:39 -07:00
}
2024-05-20 11:05:18 -07:00
2024-05-20 09:19:39 -07:00
// NewRingBuffer creates a new RingBuffer of the given capacity.
func NewRingBuffer(capacity int) *RingBuffer {
return &RingBuffer{
2024-05-29 23:33:37 -07:00
buffer: newBuffer(capacity),
2024-05-20 09:19:39 -07:00
}
}
2024-05-20 11:05:18 -07:00
2024-05-29 23:33:37 -07:00
func newBuffer(capacity int) []*TimestampStatus {
buffer := make([]*TimestampStatus, capacity)
for i := range buffer {
buffer[i] = &TimestampStatus{}
}
return buffer
}
// EnflightTimestamp adds a new timestamp to the ring buffer.
func (rb *RingBuffer) EnflightTimestamp(timestamp int64) {
2024-05-20 09:19:39 -07:00
if rb.size < len(rb.buffer) {
rb.size++
2024-05-29 23:33:37 -07:00
} else {
newBuf := newBuffer(2 * len(rb.buffer))
2024-05-29 23:33:37 -07:00
for i := 0; i < rb.size; i++ {
newBuf[i] = rb.buffer[(rb.head+len(rb.buffer)-rb.size+i)%len(rb.buffer)]
}
rb.buffer = newBuf
rb.head = rb.size
rb.size++
}
head := rb.buffer[rb.head]
head.Timestamp = timestamp
head.Acked = false
rb.head = (rb.head + 1) % len(rb.buffer)
if timestamp > rb.maxTimestamp {
rb.maxTimestamp = timestamp
2024-05-20 09:19:39 -07:00
}
}
2024-05-20 11:05:18 -07:00
2024-05-29 23:33:37 -07:00
// AckTimestamp removes the specified timestamp from the ring buffer.
func (rb *RingBuffer) AckTimestamp(timestamp int64) {
2024-05-20 09:19:39 -07:00
// Perform binary search
index := sort.Search(rb.size, func(i int) bool {
2024-05-29 23:33:37 -07:00
return rb.buffer[(rb.head+len(rb.buffer)-rb.size+i)%len(rb.buffer)].Timestamp >= timestamp
2024-05-20 09:19:39 -07:00
})
actualIndex := (rb.head + len(rb.buffer) - rb.size + index) % len(rb.buffer)
2024-05-29 23:33:37 -07:00
rb.buffer[actualIndex].Acked = true
2024-05-30 09:10:30 -07:00
// Remove all the continuously acknowledged timestamps from the buffer
2024-05-29 23:33:37 -07:00
startPos := (rb.head + len(rb.buffer) - rb.size) % len(rb.buffer)
for i := 0; i < len(rb.buffer) && rb.buffer[(startPos+i)%len(rb.buffer)].Acked; i++ {
2024-05-30 09:10:30 -07:00
t := rb.buffer[(startPos+i)%len(rb.buffer)]
if rb.maxAllAckedTs < t.Timestamp {
2024-05-30 09:41:04 -07:00
rb.size--
2024-05-30 09:10:30 -07:00
rb.maxAllAckedTs = t.Timestamp
}
2024-05-20 09:19:39 -07:00
}
}
2024-05-29 23:33:37 -07:00
// OldestAckedTimestamp returns the oldest that is already acked timestamp in the ring buffer.
func (rb *RingBuffer) OldestAckedTimestamp() int64 {
2024-05-30 09:10:30 -07:00
return rb.maxAllAckedTs
2024-05-20 09:19:39 -07:00
}
2024-05-29 23:33:37 -07:00
// Latest returns the most recently known timestamp in the ring buffer.
2024-05-20 09:19:39 -07:00
func (rb *RingBuffer) Latest() int64 {
2024-05-29 23:33:37 -07:00
return rb.maxTimestamp
2024-05-20 09:19:39 -07:00
}