package broker import ( "fmt" "sync" "time" "github.com/chrislusf/seaweedfs/weed/filer" "github.com/chrislusf/seaweedfs/weed/util/log" "github.com/chrislusf/seaweedfs/weed/pb/messaging_pb" "github.com/chrislusf/seaweedfs/weed/util/log_buffer" ) type TopicPartition struct { Namespace string Topic string Partition int32 } const ( TopicPartitionFmt = "%s/%s_%02d" ) func (tp *TopicPartition) String() string { return fmt.Sprintf(TopicPartitionFmt, tp.Namespace, tp.Topic, tp.Partition) } type TopicControl struct { sync.Mutex cond *sync.Cond subscriberCount int publisherCount int logBuffer *log_buffer.LogBuffer } type TopicManager struct { sync.Mutex topicControls map[TopicPartition]*TopicControl broker *MessageBroker } func NewTopicManager(messageBroker *MessageBroker) *TopicManager { return &TopicManager{ topicControls: make(map[TopicPartition]*TopicControl), broker: messageBroker, } } func (tm *TopicManager) buildLogBuffer(tl *TopicControl, tp TopicPartition, topicConfig *messaging_pb.TopicConfiguration) *log_buffer.LogBuffer { flushFn := func(startTime, stopTime time.Time, buf []byte) { if topicConfig.IsTransient { // return } // fmt.Printf("flushing with topic config %+v\n", topicConfig) startTime, stopTime = startTime.UTC(), stopTime.UTC() targetFile := fmt.Sprintf( "%s/%s/%s/%04d-%02d-%02d/%02d-%02d.part%02d", filer.TopicsDir, tp.Namespace, tp.Topic, startTime.Year(), startTime.Month(), startTime.Day(), startTime.Hour(), startTime.Minute(), tp.Partition, ) if err := tm.broker.appendToFile(targetFile, topicConfig, buf); err != nil { log.Infof("log write failed %s: %v", targetFile, err) } } logBuffer := log_buffer.NewLogBuffer(time.Minute, flushFn, func() { tl.cond.Broadcast() }) return logBuffer } func (tm *TopicManager) RequestLock(partition TopicPartition, topicConfig *messaging_pb.TopicConfiguration, isPublisher bool) *TopicControl { tm.Lock() defer tm.Unlock() tc, found := tm.topicControls[partition] if !found { tc = &TopicControl{} tc.cond = sync.NewCond(&tc.Mutex) tm.topicControls[partition] = tc tc.logBuffer = tm.buildLogBuffer(tc, partition, topicConfig) } if isPublisher { tc.publisherCount++ } else { tc.subscriberCount++ } return tc } func (tm *TopicManager) ReleaseLock(partition TopicPartition, isPublisher bool) { tm.Lock() defer tm.Unlock() lock, found := tm.topicControls[partition] if !found { return } if isPublisher { lock.publisherCount-- } else { lock.subscriberCount-- } if lock.subscriberCount <= 0 && lock.publisherCount <= 0 { delete(tm.topicControls, partition) lock.logBuffer.Shutdown() } } func (tm *TopicManager) ListTopicPartitions() (tps []TopicPartition) { tm.Lock() defer tm.Unlock() for k := range tm.topicControls { tps = append(tps, k) } return }