Files
base/pkg/rabbit/message.go
2026-04-10 18:25:21 +03:30

151 lines
3.3 KiB
Go

package rabbitmq
import (
"fmt"
"sync"
"time"
amqp "github.com/rabbitmq/amqp091-go"
)
type Message struct {
ID string `json:"id"`
Body []byte `json:"body"`
ContentType string `json:"content_type"`
Headers map[string]interface{} `json:"headers"`
Timestamp time.Time `json:"timestamp"`
Expiration string `json:"expiration,omitempty"`
Priority uint8 `json:"priority,omitempty"`
DeliveryMode uint8 `json:"delivery_mode"`
ReplyTo string `json:"reply_to,omitempty"`
CorrelationID string `json:"correlation_id,omitempty"`
// Internal fields for acknowledgment (not exported in JSON)
delivery *amqp.Delivery `json:"-"`
acknowledged bool `json:"-"`
ackMutex sync.Mutex `json:"-"`
}
func (m *Message) Ack() error {
m.ackMutex.Lock()
defer m.ackMutex.Unlock()
if m.delivery == nil {
return fmt.Errorf("message delivery is nil - cannot acknowledge")
}
if m.acknowledged {
return fmt.Errorf("message already acknowledged")
}
m.acknowledged = true
return m.delivery.Ack(false)
}
func (m *Message) AckMultiple() error {
m.ackMutex.Lock()
defer m.ackMutex.Unlock()
if m.delivery == nil {
return fmt.Errorf("message delivery is nil - cannot acknowledge")
}
if m.acknowledged {
return fmt.Errorf("message already acknowledged")
}
m.acknowledged = true
return m.delivery.Ack(true)
}
func (m *Message) Nack(requeue bool) error {
m.ackMutex.Lock()
defer m.ackMutex.Unlock()
if m.delivery == nil {
return fmt.Errorf("message delivery is nil - cannot nack")
}
if m.acknowledged {
return fmt.Errorf("message already acknowledged")
}
m.acknowledged = true
// Note: When requeue=false, message goes to DLQ and RabbitMQ automatically
// tracks retry count via x-death header. No need for custom IncrementRetryCount().
return m.delivery.Nack(false, requeue)
}
func (m *Message) NackMultiple(requeue bool) error {
m.ackMutex.Lock()
defer m.ackMutex.Unlock()
if m.delivery == nil {
return fmt.Errorf("message delivery is nil - cannot nack")
}
if m.acknowledged {
return fmt.Errorf("message already acknowledged")
}
m.acknowledged = true
return m.delivery.Nack(true, requeue)
}
func (m *Message) Reject(requeue bool) error {
m.ackMutex.Lock()
defer m.ackMutex.Unlock()
if m.delivery == nil {
return fmt.Errorf("message delivery is nil - cannot reject")
}
if m.acknowledged {
return fmt.Errorf("message already acknowledged")
}
m.acknowledged = true
return m.delivery.Reject(requeue)
}
func (m *Message) IsAcknowledged() bool {
m.ackMutex.Lock()
defer m.ackMutex.Unlock()
return m.acknowledged
}
func (m *Message) GetRetryCount() int64 {
if m.Headers == nil {
return 0
}
if retryCount, ok := m.Headers["x-retry-count"]; ok {
switch v := retryCount.(type) {
case int:
return int64(v)
case int64:
return v
case string:
// Try to parse string as integer
if count := parseInt(v); count >= 0 {
return count
}
}
}
xDeath, exists := m.Headers["x-death"].([]interface{})
if exists {
return xDeath[0].(amqp.Table)["count"].(int64)
}
return 0
}
func parseInt(s string) int64 {
var count int64
_, err := fmt.Sscanf(s, "%d", &count)
if err != nil {
return -1
}
return count
}