github.com/glide-im/glide@v1.6.0/pkg/subscription/subscription_impl/chan.go (about)

     1  package subscription_impl
     2  
     3  import (
     4  	"errors"
     5  	"github.com/glide-im/glide/pkg/gate"
     6  	"github.com/glide-im/glide/pkg/logger"
     7  	"github.com/glide-im/glide/pkg/messages"
     8  	"github.com/glide-im/glide/pkg/store"
     9  	"github.com/glide-im/glide/pkg/subscription"
    10  	"github.com/glide-im/glide/pkg/timingwheel"
    11  	errors2 "github.com/pkg/errors"
    12  	"reflect"
    13  	"sync"
    14  	"sync/atomic"
    15  	"time"
    16  )
    17  
    18  const (
    19  	errNotMemberOfChannel    = "not member of channel"
    20  	errPermissionDeniedWrite = "permission denied: write"
    21  	errChannelMuted          = "channel is muted"
    22  	errChannelBlocked        = "channel is blocked"
    23  )
    24  
    25  var tw = timingwheel.NewTimingWheel(time.Second, 3, 20)
    26  
    27  // messageQueueTimeout channel message push queue idle timeout.
    28  const messageQueueTimeout = time.Second * 10
    29  
    30  // ChannelSequenceStore .
    31  type ChannelSequenceStore interface {
    32  
    33  	// NextSegmentSequence return the next segment of specified channel, and segment length.
    34  	NextSegmentSequence(id subscription.ChanID, info subscription.ChanInfo) (int64, int64, error)
    35  }
    36  
    37  // SubscriberOptions is the options for the subscriber
    38  type SubscriberOptions struct {
    39  	Perm Permission
    40  }
    41  
    42  // getSubscriberOptions assertion type of `i` is *SubscribeOptions
    43  func getSubscriberOptions(i interface{}) (*SubscriberOptions, error) {
    44  	so, ok1 := i.(*SubscriberOptions)
    45  	if !ok1 {
    46  		return nil, errors.New("extra expect type: *SubscriberOptions, actual: " + reflect.TypeOf(i).String())
    47  	}
    48  	return so, nil
    49  }
    50  
    51  type SubscriberInfo struct {
    52  	Perm Permission
    53  }
    54  
    55  func (i *SubscriberInfo) canRead() bool {
    56  	return i.Perm.allows(MaskPermRead)
    57  }
    58  
    59  func (i *SubscriberInfo) canWrite() bool {
    60  	return i.Perm.allows(MaskPermWrite)
    61  }
    62  
    63  func (i *SubscriberInfo) isSystem() bool {
    64  	return i.Perm.allows(MaskPermSystem)
    65  }
    66  
    67  func (i *SubscriberInfo) isAdmin() bool {
    68  	return i.Perm.allows(MaskPermAdmin)
    69  }
    70  
    71  func (i *SubscriberInfo) update(options *SubscriberOptions) error {
    72  	i.Perm = options.Perm
    73  	return nil
    74  }
    75  
    76  func NewSubscriberInfo(so *SubscriberOptions) *SubscriberInfo {
    77  	return &SubscriberInfo{
    78  		Perm: so.Perm,
    79  	}
    80  }
    81  
    82  type Channel struct {
    83  	id subscription.ChanID
    84  
    85  	seq       int64
    86  	seqRemain int64
    87  
    88  	messages chan *PublishMessage
    89  
    90  	queueRunning int32
    91  	queued       int32
    92  
    93  	sleepTimer *timingwheel.Task
    94  
    95  	activeAt    time.Time
    96  	mu          *sync.RWMutex
    97  	subscribers map[subscription.SubscriberID]*SubscriberInfo
    98  	info        *subscription.ChanInfo
    99  
   100  	store    store.SubscriptionStore
   101  	seqStore ChannelSequenceStore
   102  	gate     gate.DefaultGateway
   103  }
   104  
   105  func NewChannel(chanID subscription.ChanID, gate gate.DefaultGateway,
   106  	store store.SubscriptionStore, seqStore ChannelSequenceStore) (*Channel, error) {
   107  
   108  	ret := &Channel{
   109  		id:          chanID,
   110  		messages:    make(chan *PublishMessage, 100),
   111  		sleepTimer:  tw.After(messageQueueTimeout),
   112  		mu:          &sync.RWMutex{},
   113  		subscribers: map[subscription.SubscriberID]*SubscriberInfo{},
   114  		info:        &subscription.ChanInfo{},
   115  		store:       store,
   116  		seqStore:    seqStore,
   117  		gate:        gate,
   118  	}
   119  	err := ret.loadSeq()
   120  	if err != nil {
   121  		return nil, err
   122  	}
   123  	return ret, nil
   124  }
   125  
   126  func (g *Channel) Update(ci *subscription.ChanInfo) error {
   127  	g.info.Blocked = ci.Blocked
   128  	g.info.Muted = ci.Muted
   129  	return nil
   130  }
   131  
   132  func (g *Channel) Subscribe(id subscription.SubscriberID, extra interface{}) error {
   133  	so, err := getSubscriberOptions(extra)
   134  	if err != nil {
   135  		return err
   136  	}
   137  
   138  	if g.info.Closed {
   139  		return errors.New("channel is closed")
   140  	}
   141  	if g.info.Blocked {
   142  		return errors.New("channel is blocked")
   143  	}
   144  
   145  	g.mu.Lock()
   146  	defer g.mu.Unlock()
   147  
   148  	sb, ok := g.subscribers[id]
   149  	if ok {
   150  		return sb.update(so)
   151  	}
   152  	g.subscribers[id] = NewSubscriberInfo(so)
   153  	return nil
   154  }
   155  
   156  func (g *Channel) Unsubscribe(id subscription.SubscriberID) error {
   157  	g.mu.Lock()
   158  	defer g.mu.Unlock()
   159  
   160  	_, ok := g.subscribers[id]
   161  	if !ok {
   162  		return errors.New(subscription.ErrNotSubscribed)
   163  	}
   164  	delete(g.subscribers, id)
   165  	return nil
   166  }
   167  
   168  func (g *Channel) Publish(msg subscription.Message) error {
   169  	if g.info.Closed {
   170  		return errors.New("channel closed")
   171  	}
   172  	message, ok := msg.(*PublishMessage)
   173  	if !ok {
   174  		return errors.New("unexpected message type, expect: *subscription.PublishMessage, actual:" + reflect.TypeOf(msg).String())
   175  	}
   176  
   177  	if !isValidMessageType(message.Type) {
   178  		return errors.New(errUnknownMessageType)
   179  	}
   180  
   181  	g.mu.RLock()
   182  	s, exist := g.subscribers[message.From]
   183  	g.mu.RUnlock()
   184  
   185  	if !exist {
   186  		return errors.New(errNotMemberOfChannel)
   187  	}
   188  	if !s.canWrite() {
   189  		return errors.New(errPermissionDeniedWrite)
   190  	}
   191  	if g.info.Muted {
   192  		if !s.isSystem() || !s.isAdmin() {
   193  			return errors.New(errChannelMuted)
   194  		}
   195  	}
   196  	if g.info.Blocked {
   197  		if !s.isSystem() {
   198  			return errors.New(errChannelBlocked)
   199  		}
   200  	}
   201  
   202  	switch message.Type {
   203  	case TypeNotify:
   204  		return g.enqueueNotify(message)
   205  	case TypeMessage:
   206  		err := g.enqueue(message)
   207  		return err
   208  	default:
   209  		return errors.New(errUnknownMessageType)
   210  	}
   211  }
   212  
   213  func (g *Channel) Close() error {
   214  	g.mu.Lock()
   215  	defer g.mu.Unlock()
   216  
   217  	g.info.Closed = true
   218  
   219  	close(g.messages)
   220  	g.subscribers = map[subscription.SubscriberID]*SubscriberInfo{}
   221  
   222  	if g.queued > 0 {
   223  		logger.D("chan %s closed, %d messages dropped", g.id, g.queued)
   224  	}
   225  	return nil
   226  }
   227  
   228  func (g *Channel) enqueueNotify(msg *PublishMessage) error {
   229  	select {
   230  	case g.messages <- msg:
   231  		atomic.AddInt32(&g.queued, 1)
   232  	default:
   233  		return errors.New("notify message queue is full")
   234  	}
   235  	return g.checkMsgQueue()
   236  }
   237  
   238  func (g *Channel) enqueue(m *PublishMessage) error {
   239  
   240  	cm, err := m.GetChatMessage()
   241  	if err != nil {
   242  		return errors2.Wrap(err, "enqueue message deserialize body error")
   243  	}
   244  	m.Seq, err = g.nextSeq()
   245  	if err != nil {
   246  		return err
   247  	}
   248  	cm.Seq = m.Seq
   249  	m.Message.Data = messages.NewData(&cm)
   250  
   251  	if m.Type == TypeMessage {
   252  		err = g.store.StoreChannelMessage(g.id, cm)
   253  		if err != nil {
   254  			return errors2.Wrap(err, "store channel message error")
   255  		}
   256  	}
   257  
   258  	select {
   259  	case g.messages <- m:
   260  		atomic.AddInt32(&g.queued, 1)
   261  	default:
   262  		return errors.New("too many messages,the group message queue is full")
   263  	}
   264  	if err = g.checkMsgQueue(); err != nil {
   265  		return err
   266  	}
   267  	return nil
   268  }
   269  
   270  func (g *Channel) checkMsgQueue() error {
   271  
   272  	if atomic.LoadInt32(&g.queueRunning) != 0 {
   273  		return nil
   274  	}
   275  
   276  	atomic.StoreInt32(&g.queueRunning, 1)
   277  	go func() {
   278  		defer func() {
   279  			err := recover()
   280  			if err != nil {
   281  				atomic.StoreInt32(&g.queueRunning, 0)
   282  				logger.E("message queue panic: %v", err)
   283  			}
   284  		}()
   285  
   286  		g.sleepTimer = tw.After(messageQueueTimeout)
   287  		for {
   288  			select {
   289  			case <-g.sleepTimer.C:
   290  				g.sleepTimer.Cancel()
   291  				if g.activeAt.Add(messageQueueTimeout).Before(time.Now()) {
   292  					goto REST
   293  				} else {
   294  					g.sleepTimer = tw.After(messageQueueTimeout)
   295  				}
   296  			case m := <-g.messages:
   297  				if m == nil {
   298  					goto REST
   299  				}
   300  				atomic.AddInt32(&g.queued, -1)
   301  				g.activeAt = time.Now()
   302  				g.push(m)
   303  			}
   304  		}
   305  	REST:
   306  		dropped := 0
   307  		if atomic.LoadInt32(&g.queued) > 0 {
   308  			for {
   309  				_, ok := <-g.messages
   310  				if !ok {
   311  					break
   312  				}
   313  				dropped++
   314  				atomic.StoreInt32(&g.queued, -1)
   315  			}
   316  		}
   317  		if dropped > 0 {
   318  			logger.W("chan %s message queue stopped, %d message(s) have been dropped", g.id, dropped)
   319  		} else {
   320  			logger.D("chan %s message queue stopped", g.id)
   321  		}
   322  		atomic.StoreInt32(&g.queued, 0)
   323  		atomic.StoreInt32(&g.queueRunning, 0)
   324  	}()
   325  	return nil
   326  }
   327  
   328  func (g *Channel) push(message *PublishMessage) {
   329  	logger.I("chan %s push message: %v", g.id, message.Message)
   330  
   331  	g.mu.RLock()
   332  	defer g.mu.RUnlock()
   333  
   334  	// TODO recycler use
   335  	var received = map[subscription.SubscriberID]interface{}{}
   336  
   337  	if message.To != nil {
   338  		for _, id := range message.To {
   339  			received[id] = nil
   340  		}
   341  	}
   342  
   343  	for subscriberID, sInfo := range g.subscribers {
   344  		if received != nil && len(received) > 0 {
   345  			_, contained := received[subscriberID]
   346  			if !contained {
   347  				continue
   348  			}
   349  		}
   350  		if !sInfo.canRead() {
   351  			continue
   352  		}
   353  		err := g.gate.EnqueueMessage(gate.ID(subscriberID), message.Message)
   354  		if err != nil {
   355  			logger.E("chan %s push message to subscribe %s error: %v", g.id, subscriberID, err)
   356  		}
   357  	}
   358  }
   359  
   360  func (g *Channel) nextSeq() (int64, error) {
   361  	if atomic.AddInt64(&g.seqRemain, -1) <= 0 {
   362  		err := g.loadSeq()
   363  		if err != nil {
   364  			return 0, err
   365  		}
   366  		atomic.AddInt64(&g.seq, -1)
   367  	}
   368  	return atomic.AddInt64(&g.seq, 1), nil
   369  }
   370  
   371  func (g *Channel) loadSeq() error {
   372  	seq, length, err := g.seqStore.NextSegmentSequence(g.id, *g.info)
   373  	if err != nil {
   374  		return err
   375  	}
   376  	atomic.StoreInt64(&g.seqRemain, length)
   377  	// because seq increment before set to message
   378  	atomic.StoreInt64(&g.seq, seq)
   379  	return nil
   380  }