Example #1
0
/*
Parse a gilmour *Message and for subscribers of this topic do the following:
	* If subscriber is one shot, unsubscribe the subscriber to prevent subscribers from re-execution.
	* If subscriber belongs to a group, try acquiring a lock via backend to ensure group exclusivity.
	* If all conditions suffice spin up a new goroutine for each subscription.
*/
func (g *Gilmour) processMessage(msg *proto.Packet) {
	subs, ok := g.getSubscribers(msg.GetPattern())
	if !ok || len(subs) == 0 {
		ui.Warn("*Message cannot be processed. No subs found for key %v", msg.GetPattern())
		return
	}

	m, err := parseMessage(msg.GetData())
	if err != nil {
		ui.Alert(err.Error())
		return
	}

	for _, s := range subs {

		opts := s.GetOpts()
		if opts != nil && opts.isOneShot() {
			ui.Message("Unsubscribing one shot response topic %v", msg.GetTopic())
			go g.UnsubscribeReply(msg.GetPattern(), s)
		}

		if opts.GetGroup() != "" && opts.shouldSendResponse() {
			if !g.backend.AcquireGroupLock(opts.GetGroup(), m.GetSender()) {
				ui.Warn(
					"Unable to acquire Lock. Topic %v Group %v Sender %v",
					msg.GetTopic(), opts.GetGroup(), m.GetSender(),
				)
				continue
			}
		}

		go g.handleRequest(s, msg.GetTopic(), m)
	}
}
Example #2
0
func (g *Gilmour) sendTimeout(senderId, channel string) {
	msg := g.timeoutMessage(senderId)
	if err := g.publish(channel, msg); err != nil {
		ui.Alert(err.Error())
	}
}
Example #3
0
func (g *Gilmour) handleRequest(s *Subscription, topic string, m *Message) {
	senderId := m.GetSender()

	req := &Request{topic, m}
	res := NewMessage()
	res.setSender(proto.ResponseTopic(senderId))

	done := make(chan bool, 1)

	//Executing Request
	go func(done chan<- bool) {

		// Schedule a function to recover in case handler runs into an error.
		// Read more: https://gist.github.com/meson10/d56eface6f87c664d07d

		defer func() {
			err := recover()
			if err == nil {
				return
			}

			const size = 4096
			buf := make([]byte, size)
			buf = buf[:runtime.Stack(buf, false)]
			buffer := string(buf)
			res.SetData(buffer).SetCode(500)

			done <- true

		}()

		s.GetHandler()(req, res)
		done <- true
	}(done)

	// Start a timeout handler, which writes on the Done channel, ahead of the
	// handler. This might result in a RACE condition, as there is no way to
	// kill a goroutine, since they are not preemptive.

	timeout := s.GetOpts().GetTimeout()
	time.AfterFunc(time.Duration(timeout)*time.Second, func() {
		done <- false
	})

	status := <-done

	if s.GetOpts().shouldSendResponse() {
		if status == false {
			g.sendTimeout(senderId, res.GetSender())
		} else {
			if res.GetCode() == 0 {
				res.SetCode(200)
			}

			if err := g.publish(res.GetSender(), res); err != nil {
				ui.Alert(err.Error())
			}
		}

	} else if status == false {
		// Inform the error catcher, If there is no handler for this Request
		// but the request had failed. This is automatically handled in case
		// of a response being written via Publisher.
		request := string(req.bytes())
		errMsg := proto.MakeError(499, topic, request, "", req.Sender(), "")
		g.reportError(errMsg)
	}
}