github.com/consideritdone/landslidecore@v0.0.0-20230718131026-a8b21c5cf8a7/libs/pubsub/subscription.go (about) 1 package pubsub 2 3 import ( 4 "errors" 5 6 tmsync "github.com/consideritdone/landslidecore/libs/sync" 7 ) 8 9 var ( 10 // ErrUnsubscribed is returned by Err when a client unsubscribes. 11 ErrUnsubscribed = errors.New("client unsubscribed") 12 13 // ErrOutOfCapacity is returned by Err when a client is not pulling messages 14 // fast enough. Note the client's subscription will be terminated. 15 ErrOutOfCapacity = errors.New("internal subscription event buffer is out of capacity") 16 ) 17 18 // A Subscription represents a client subscription for a particular query and 19 // consists of three things: 20 // 1) channel onto which messages and events are published 21 // 2) channel which is closed if a client is too slow or choose to unsubscribe 22 // 3) err indicating the reason for (2) 23 type Subscription struct { 24 out chan Message 25 26 canceled chan struct{} 27 mtx tmsync.RWMutex 28 err error 29 } 30 31 // NewSubscription returns a new subscription with the given outCapacity. 32 func NewSubscription(outCapacity int) *Subscription { 33 return &Subscription{ 34 out: make(chan Message, outCapacity), 35 canceled: make(chan struct{}), 36 } 37 } 38 39 // Out returns a channel onto which messages and events are published. 40 // Unsubscribe/UnsubscribeAll does not close the channel to avoid clients from 41 // receiving a nil message. 42 func (s *Subscription) Out() <-chan Message { 43 return s.out 44 } 45 46 // nolint: misspell 47 // Cancelled returns a channel that's closed when the subscription is 48 // terminated and supposed to be used in a select statement. 49 func (s *Subscription) Cancelled() <-chan struct{} { 50 return s.canceled 51 } 52 53 // Err returns nil if the channel returned is not yet closed. 54 // If the channel is closed, Err returns a non-nil error explaining why: 55 // - ErrUnsubscribed if the subscriber choose to unsubscribe, 56 // - ErrOutOfCapacity if the subscriber is not pulling messages fast enough 57 // and the channel returned by Out became full, 58 // 59 // After Err returns a non-nil error, successive calls to Err return the same 60 // error. 61 func (s *Subscription) Err() error { 62 s.mtx.RLock() 63 defer s.mtx.RUnlock() 64 return s.err 65 } 66 67 func (s *Subscription) cancel(err error) { 68 s.mtx.Lock() 69 s.err = err 70 s.mtx.Unlock() 71 close(s.canceled) 72 } 73 74 // Message glues data and events together. 75 type Message struct { 76 data interface{} 77 events map[string][]string 78 } 79 80 func NewMessage(data interface{}, events map[string][]string) Message { 81 return Message{data, events} 82 } 83 84 // Data returns an original data published. 85 func (msg Message) Data() interface{} { 86 return msg.data 87 } 88 89 // Events returns events, which matched the client's query. 90 func (msg Message) Events() map[string][]string { 91 return msg.events 92 }