github.com/leg100/ots@v0.0.7-0.20210919080622-034055ced4bd/inmem/event.go (about)

     1  package inmem
     2  
     3  import (
     4  	"sync"
     5  
     6  	"github.com/go-logr/logr"
     7  	"github.com/leg100/ots"
     8  )
     9  
    10  // EventBufferSize is the buffer size of the channel for each subscription.
    11  const EventBufferSize = 16
    12  
    13  var _ ots.EventService = (*EventService)(nil)
    14  
    15  type EventService struct {
    16  	mu   sync.Mutex
    17  	subs map[string]*Subscription
    18  	logr.Logger
    19  }
    20  
    21  // NewEventService returns a new instance of EventService.
    22  func NewEventService(logger logr.Logger) *EventService {
    23  	return &EventService{
    24  		subs:   make(map[string]*Subscription),
    25  		Logger: logger.WithValues("component", "event_service"),
    26  	}
    27  }
    28  
    29  func (e *EventService) Publish(event ots.Event) {
    30  	for _, sub := range e.subs {
    31  		select {
    32  		case sub.c <- event:
    33  		default:
    34  			e.unsubscribe(sub)
    35  		}
    36  	}
    37  }
    38  
    39  func (e *EventService) Subscribe(id string) ots.Subscription {
    40  	// Create new subscription
    41  	sub := &Subscription{
    42  		service: e,
    43  		c:       make(chan ots.Event, EventBufferSize),
    44  	}
    45  
    46  	// Add to list of user's subscriptions. Subscriptions are stored as a map
    47  	// for each user so we can easily delete them.
    48  	e.subs[id] = sub
    49  
    50  	e.Info("subscription created", "subscriber", id)
    51  
    52  	return sub
    53  }
    54  
    55  // Unsubscribe disconnects sub from the service.
    56  func (e *EventService) Unsubscribe(sub *Subscription) {
    57  	e.mu.Lock()
    58  	defer e.mu.Unlock()
    59  	e.unsubscribe(sub)
    60  }
    61  
    62  func (e *EventService) unsubscribe(sub *Subscription) {
    63  	// Only close the underlying channel once. Otherwise Go will panic.
    64  	sub.once.Do(func() {
    65  		close(sub.c)
    66  	})
    67  
    68  	delete(e.subs, sub.id)
    69  }
    70  
    71  // Subscription represents a stream of events.
    72  type Subscription struct {
    73  	service *EventService // service subscription was created from
    74  	id      string        // Uniquely identifies subscription
    75  
    76  	c    chan ots.Event // channel of events
    77  	once sync.Once      // ensures c only closed once
    78  }
    79  
    80  // Close disconnects the subscription from the service it was created from.
    81  func (s *Subscription) Close() error {
    82  	s.service.Unsubscribe(s)
    83  	return nil
    84  }
    85  
    86  // C returns a receive-only channel of user-related events.
    87  func (s *Subscription) C() <-chan ots.Event {
    88  	return s.c
    89  }