github.com/tickoalcantara12/micro/v3@v3.0.0-20221007104245-9d75b9bcbab9/service/events/stream/memory/memory.go (about)

     1  // Licensed under the Apache License, Version 2.0 (the "License");
     2  // you may not use this file except in compliance with the License.
     3  // You may obtain a copy of the License at
     4  //
     5  //     https://www.apache.org/licenses/LICENSE-2.0
     6  //
     7  // Unless required by applicable law or agreed to in writing, software
     8  // distributed under the License is distributed on an "AS IS" BASIS,
     9  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    10  // See the License for the specific language governing permissions and
    11  // limitations under the License.
    12  //
    13  // Original source: github.com/micro/go-micro/v3/events/stream/memory/memory.go
    14  
    15  package memory
    16  
    17  import (
    18  	"encoding/json"
    19  	"fmt"
    20  	"sync"
    21  	"time"
    22  
    23  	"github.com/google/uuid"
    24  	"github.com/tickoalcantara12/micro/v3/service/events"
    25  	"github.com/tickoalcantara12/micro/v3/service/logger"
    26  	"github.com/tickoalcantara12/micro/v3/service/store"
    27  	"github.com/tickoalcantara12/micro/v3/service/store/memory"
    28  	"github.com/pkg/errors"
    29  )
    30  
    31  // NewStream returns an initialized memory stream
    32  func NewStream(opts ...Option) (events.Stream, error) {
    33  	// parse the options
    34  	var options Options
    35  	for _, o := range opts {
    36  		o(&options)
    37  	}
    38  	if options.Store == nil {
    39  		options.Store = memory.NewStore()
    40  	}
    41  
    42  	return &mem{store: options.Store}, nil
    43  }
    44  
    45  type subscriber struct {
    46  	Group   string
    47  	Topic   string
    48  	Channel chan events.Event
    49  
    50  	sync.RWMutex
    51  	retryMap   map[string]int
    52  	retryLimit int
    53  	autoAck    bool
    54  	ackWait    time.Duration
    55  }
    56  
    57  type mem struct {
    58  	store store.Store
    59  
    60  	subs []*subscriber
    61  	sync.RWMutex
    62  }
    63  
    64  func (m *mem) Publish(topic string, msg interface{}, opts ...events.PublishOption) error {
    65  	// validate the topic
    66  	if len(topic) == 0 {
    67  		return events.ErrMissingTopic
    68  	}
    69  
    70  	// parse the options
    71  	options := events.PublishOptions{
    72  		Timestamp: time.Now(),
    73  	}
    74  	for _, o := range opts {
    75  		o(&options)
    76  	}
    77  
    78  	// encode the message if it's not already encoded
    79  	var payload []byte
    80  	if p, ok := msg.([]byte); ok {
    81  		payload = p
    82  	} else {
    83  		p, err := json.Marshal(msg)
    84  		if err != nil {
    85  			return events.ErrEncodingMessage
    86  		}
    87  		payload = p
    88  	}
    89  
    90  	// construct the event
    91  	event := &events.Event{
    92  		ID:        uuid.New().String(),
    93  		Topic:     topic,
    94  		Timestamp: options.Timestamp,
    95  		Metadata:  options.Metadata,
    96  		Payload:   payload,
    97  	}
    98  
    99  	// serialize the event to bytes
   100  	bytes, err := json.Marshal(event)
   101  	if err != nil {
   102  		return errors.Wrap(err, "Error encoding event")
   103  	}
   104  
   105  	// write to the store
   106  	key := fmt.Sprintf("%v/%v", event.Topic, event.ID)
   107  	if err := m.store.Write(&store.Record{Key: key, Value: bytes}); err != nil {
   108  		return errors.Wrap(err, "Error writing event to store")
   109  	}
   110  
   111  	// send to the subscribers async
   112  	go m.handleEvent(event)
   113  
   114  	return nil
   115  }
   116  
   117  func (m *mem) Consume(topic string, opts ...events.ConsumeOption) (<-chan events.Event, error) {
   118  	// validate the topic
   119  	if len(topic) == 0 {
   120  		return nil, events.ErrMissingTopic
   121  	}
   122  
   123  	// parse the options
   124  	options := events.ConsumeOptions{
   125  		Group:   uuid.New().String(),
   126  		AutoAck: true,
   127  	}
   128  	for _, o := range opts {
   129  		o(&options)
   130  	}
   131  	// TODO RetryLimit
   132  
   133  	// setup the subscriber
   134  	sub := &subscriber{
   135  		Channel:    make(chan events.Event),
   136  		Topic:      topic,
   137  		Group:      options.Group,
   138  		retryMap:   map[string]int{},
   139  		autoAck:    true,
   140  		retryLimit: options.GetRetryLimit(),
   141  	}
   142  
   143  	if !options.AutoAck {
   144  		if options.AckWait == 0 {
   145  			return nil, fmt.Errorf("invalid AckWait passed, should be positive integer")
   146  		}
   147  		sub.autoAck = options.AutoAck
   148  		sub.ackWait = options.AckWait
   149  	}
   150  
   151  	// register the subscriber
   152  	m.Lock()
   153  	m.subs = append(m.subs, sub)
   154  	m.Unlock()
   155  
   156  	// lookup previous events if the start time option was passed
   157  	if options.Offset.Unix() > 0 {
   158  		go m.lookupPreviousEvents(sub, options.Offset)
   159  	}
   160  
   161  	// return the channel
   162  	return sub.Channel, nil
   163  }
   164  
   165  // lookupPreviousEvents finds events for a subscriber which occurred before a given time and sends
   166  // them into the subscribers channel
   167  func (m *mem) lookupPreviousEvents(sub *subscriber, startTime time.Time) {
   168  	// lookup all events which match the topic (a blank topic will return all results)
   169  	recs, err := m.store.Read(sub.Topic+"/", store.ReadPrefix())
   170  	if err != nil && logger.V(logger.ErrorLevel, logger.DefaultLogger) {
   171  		logger.Errorf("Error looking up previous events: %v", err)
   172  		return
   173  	} else if err != nil {
   174  		return
   175  	}
   176  
   177  	// loop through the records and send it to the channel if it matches
   178  	for _, r := range recs {
   179  		var ev events.Event
   180  		if err := json.Unmarshal(r.Value, &ev); err != nil {
   181  			continue
   182  		}
   183  		if ev.Timestamp.Unix() < startTime.Unix() {
   184  			continue
   185  		}
   186  		sendEvent(&ev, sub)
   187  	}
   188  }
   189  
   190  // handleEvents sends the event to any registered subscribers.
   191  func (m *mem) handleEvent(ev *events.Event) {
   192  	m.RLock()
   193  	subs := m.subs
   194  	m.RUnlock()
   195  
   196  	// filteredSubs is a KV map of the queue name and subscribers. This is used to prevent a message
   197  	// being sent to two subscribers with the same queue.
   198  	filteredSubs := map[string]*subscriber{}
   199  
   200  	// filter down to subscribers who are interested in this topic
   201  	for _, sub := range subs {
   202  		if len(sub.Topic) == 0 || sub.Topic == ev.Topic {
   203  			filteredSubs[sub.Group] = sub
   204  		}
   205  	}
   206  
   207  	// send the message to each channel async (since one channel might be blocked)
   208  	for _, sub := range filteredSubs {
   209  		sendEvent(ev, sub)
   210  	}
   211  }
   212  
   213  func sendEvent(ev *events.Event, sub *subscriber) {
   214  	go func(s *subscriber) {
   215  		evCopy := *ev
   216  		if s.autoAck {
   217  			s.Channel <- evCopy
   218  			return
   219  		}
   220  		evCopy.SetAckFunc(ackFunc(s, evCopy))
   221  		evCopy.SetNackFunc(nackFunc(s, evCopy))
   222  		s.retryMap[evCopy.ID] = 0
   223  		tick := time.NewTicker(s.ackWait)
   224  		defer tick.Stop()
   225  		for range tick.C {
   226  			s.Lock()
   227  			count, ok := s.retryMap[evCopy.ID]
   228  			s.Unlock()
   229  			if !ok {
   230  				// success
   231  				break
   232  			}
   233  
   234  			if s.retryLimit > -1 && count > s.retryLimit {
   235  				if logger.V(logger.ErrorLevel, logger.DefaultLogger) {
   236  					logger.Errorf("Message retry limit reached, discarding: %v %d %d", evCopy.ID, count, s.retryLimit)
   237  				}
   238  				s.Lock()
   239  				delete(s.retryMap, evCopy.ID)
   240  				s.Unlock()
   241  				return
   242  			}
   243  			s.Channel <- evCopy
   244  			s.Lock()
   245  			s.retryMap[evCopy.ID] = count + 1
   246  			s.Unlock()
   247  		}
   248  	}(sub)
   249  }
   250  
   251  func ackFunc(s *subscriber, evCopy events.Event) func() error {
   252  	return func() error {
   253  		s.Lock()
   254  		delete(s.retryMap, evCopy.ID)
   255  		s.Unlock()
   256  		return nil
   257  	}
   258  }
   259  
   260  func nackFunc(s *subscriber, evCopy events.Event) func() error {
   261  	return func() error {
   262  		return nil
   263  	}
   264  }