2023-12-11 20:05:54 +00:00
|
|
|
package sub_coordinator
|
|
|
|
|
|
|
|
import (
|
|
|
|
cmap "github.com/orcaman/concurrent-map/v2"
|
|
|
|
"github.com/seaweedfs/seaweedfs/weed/mq/pub_balancer"
|
|
|
|
"github.com/seaweedfs/seaweedfs/weed/pb/mq_pb"
|
|
|
|
)
|
|
|
|
|
|
|
|
type TopicConsumerGroups struct {
|
|
|
|
// map a consumer group name to a consumer group
|
|
|
|
ConsumerGroups cmap.ConcurrentMap[string, *ConsumerGroup]
|
|
|
|
}
|
|
|
|
|
|
|
|
// Coordinator coordinates the instances in the consumer group for one topic.
|
|
|
|
// It is responsible for:
|
|
|
|
// 1. (Maybe) assigning partitions when a consumer instance is up/down.
|
|
|
|
|
|
|
|
type Coordinator struct {
|
|
|
|
// map topic name to consumer groups
|
|
|
|
TopicSubscribers cmap.ConcurrentMap[string, *TopicConsumerGroups]
|
2023-12-22 19:33:50 +00:00
|
|
|
balancer *pub_balancer.Balancer
|
2023-12-11 20:05:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func NewCoordinator(balancer *pub_balancer.Balancer) *Coordinator {
|
|
|
|
return &Coordinator{
|
|
|
|
TopicSubscribers: cmap.New[*TopicConsumerGroups](),
|
2023-12-22 19:33:50 +00:00
|
|
|
balancer: balancer,
|
2023-12-11 20:05:54 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-01-03 21:30:30 +00:00
|
|
|
func (c *Coordinator) GetTopicConsumerGroups(topic *mq_pb.Topic, createIfMissing bool) *TopicConsumerGroups {
|
2023-12-11 20:05:54 +00:00
|
|
|
topicName := toTopicName(topic)
|
|
|
|
tcg, _ := c.TopicSubscribers.Get(topicName)
|
2024-01-03 21:30:30 +00:00
|
|
|
if tcg == nil && createIfMissing{
|
2023-12-11 20:05:54 +00:00
|
|
|
tcg = &TopicConsumerGroups{
|
|
|
|
ConsumerGroups: cmap.New[*ConsumerGroup](),
|
|
|
|
}
|
2024-01-03 21:30:30 +00:00
|
|
|
if !c.TopicSubscribers.SetIfAbsent(topicName, tcg) {
|
|
|
|
tcg, _ = c.TopicSubscribers.Get(topicName)
|
|
|
|
}
|
2023-12-11 20:05:54 +00:00
|
|
|
}
|
|
|
|
return tcg
|
|
|
|
}
|
|
|
|
func (c *Coordinator) RemoveTopic(topic *mq_pb.Topic) {
|
|
|
|
topicName := toTopicName(topic)
|
|
|
|
c.TopicSubscribers.Remove(topicName)
|
|
|
|
}
|
|
|
|
|
|
|
|
func toTopicName(topic *mq_pb.Topic) string {
|
|
|
|
topicName := topic.Namespace + "." + topic.Name
|
|
|
|
return topicName
|
|
|
|
}
|
|
|
|
|
2023-12-22 19:33:50 +00:00
|
|
|
func (c *Coordinator) AddSubscriber(consumerGroup, consumerGroupInstance string, topic *mq_pb.Topic) *ConsumerGroupInstance {
|
2024-01-03 21:30:30 +00:00
|
|
|
tcg := c.GetTopicConsumerGroups(topic, true)
|
2023-12-11 20:05:54 +00:00
|
|
|
cg, _ := tcg.ConsumerGroups.Get(consumerGroup)
|
|
|
|
if cg == nil {
|
2023-12-29 04:35:15 +00:00
|
|
|
cg = NewConsumerGroup(topic, c.balancer)
|
2024-01-03 21:30:30 +00:00
|
|
|
if !tcg.ConsumerGroups.SetIfAbsent(consumerGroup, cg){
|
|
|
|
cg, _ = tcg.ConsumerGroups.Get(consumerGroup)
|
|
|
|
}
|
2023-12-11 20:05:54 +00:00
|
|
|
}
|
|
|
|
cgi, _ := cg.ConsumerGroupInstances.Get(consumerGroupInstance)
|
|
|
|
if cgi == nil {
|
|
|
|
cgi = NewConsumerGroupInstance(consumerGroupInstance)
|
2024-01-03 21:30:30 +00:00
|
|
|
if !cg.ConsumerGroupInstances.SetIfAbsent(consumerGroupInstance, cgi){
|
|
|
|
cgi, _ = cg.ConsumerGroupInstances.Get(consumerGroupInstance)
|
|
|
|
}
|
2023-12-11 20:05:54 +00:00
|
|
|
}
|
|
|
|
cg.OnAddConsumerGroupInstance(consumerGroupInstance, topic)
|
|
|
|
return cgi
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Coordinator) RemoveSubscriber(consumerGroup, consumerGroupInstance string, topic *mq_pb.Topic) {
|
2024-01-03 21:30:30 +00:00
|
|
|
tcg := c.GetTopicConsumerGroups(topic, false)
|
2023-12-11 20:05:54 +00:00
|
|
|
if tcg == nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
cg, _ := tcg.ConsumerGroups.Get(consumerGroup)
|
|
|
|
if cg == nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
cg.ConsumerGroupInstances.Remove(consumerGroupInstance)
|
|
|
|
cg.OnRemoveConsumerGroupInstance(consumerGroupInstance, topic)
|
|
|
|
if cg.ConsumerGroupInstances.Count() == 0 {
|
|
|
|
tcg.ConsumerGroups.Remove(consumerGroup)
|
|
|
|
}
|
|
|
|
if tcg.ConsumerGroups.Count() == 0 {
|
|
|
|
c.RemoveTopic(topic)
|
|
|
|
}
|
|
|
|
}
|
2024-01-03 21:30:30 +00:00
|
|
|
|
|
|
|
func (c *Coordinator) OnPartitionChange(topic *mq_pb.Topic, assignments []*mq_pb.BrokerPartitionAssignment) {
|
|
|
|
tcg, _ := c.TopicSubscribers.Get(toTopicName(topic))
|
|
|
|
if tcg == nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
for _, cg := range tcg.ConsumerGroups.Items() {
|
|
|
|
cg.OnPartitionListChange(assignments)
|
|
|
|
}
|
|
|
|
}
|