Example #1
0
func (me *broker) _consumer_routine_(
	quit <-chan int,
	wait *sync.WaitGroup,
	events chan<- *dingo.Event,
	tasks chan<- []byte,
	receipts <-chan *dingo.TaskReceipt,
	name string,
) {
	defer wait.Done()

	conn := me.pool.Get()
	defer conn.Close()

	qn := fmt.Sprintf("%v.%v", _redisTaskQueue, name)

	for {
		select {
		case _, _ = <-quit:
			goto clean
		default:
			// blocking call on redis server
			reply, err := conn.Do("BRPOP", qn, me.cfg.GetListenTimeout())
			if err != nil {
				events <- dingo.NewEventFromError(dingo.ObjT.Consumer|dingo.ObjT.NamedConsumer, err)
				break
			}

			if reply == nil {
				// timeout, go for next round
				break
			}

			// the return value from BRPOP should be
			// an slice with length 2
			v, ok := reply.([]interface{})
			if !ok {
				events <- dingo.NewEventFromError(
					dingo.ObjT.Consumer|dingo.ObjT.NamedConsumer,
					errors.New(fmt.Sprintf("invalid reply: %v", reply)),
				)
				break
			}
			if len(v) != 2 {
				events <- dingo.NewEventFromError(
					dingo.ObjT.Consumer|dingo.ObjT.NamedConsumer,
					errors.New(fmt.Sprintf("invalid reply: %v", reply)),
				)
				break
			}

			b, ok := v[1].([]byte)
			if !ok {
				events <- dingo.NewEventFromError(
					dingo.ObjT.Consumer|dingo.ObjT.NamedConsumer,
					errors.New(fmt.Sprintf("invalid reply: %v", reply)),
				)
				break
			}

			h, err := dingo.DecodeHeader(b)
			if err != nil {
				events <- dingo.NewEventFromError(dingo.ObjT.Consumer|dingo.ObjT.NamedConsumer, err)
				break
			}

			tasks <- b
			rcpt, ok := <-receipts
			if !ok {
				goto clean
			}

			if rcpt.ID != h.ID() {
				events <- dingo.NewEventFromError(
					dingo.ObjT.Consumer|dingo.ObjT.NamedConsumer,
					errors.New(fmt.Sprintf("expected: %v, received: %v", h, rcpt)),
				)
				break
			}
		}
	}
clean:
	return
}
Example #2
0
func (me *broker) _consumer_routine_(
	quit <-chan int,
	wait *sync.WaitGroup,
	events chan<- *dingo.Event,
	tasks chan<- []byte,
	receipts <-chan *dingo.TaskReceipt,
	ci *AmqpChannel,
	queueName string,
) {
	defer wait.Done()
	defer me.receiver.ReleaseChannel(ci)

	// acquire an tag
	id := <-me.consumerTags
	tag := fmt.Sprintf("dingo.consumer.%d", id)

	// return id
	defer func(id int) {
		me.consumerTags <- id
	}(id)

	dv, err := ci.Channel.Consume(
		queueName,
		tag,   // consumer Tag
		false, // autoAck
		false, // exclusive
		false, // noLocal
		false, // noWait
		nil,   // args
	)
	if err != nil {
		events <- dingo.NewEventFromError(dingo.ObjT.Consumer|dingo.ObjT.NamedConsumer, err)
		return
	}

	for {
		select {
		case d, ok := <-dv:
			if !ok {
				goto clean
			}

			ok = func() (ok bool) {
				var (
					reply *dingo.TaskReceipt
					err   error
				)
				defer func() {
					if err != nil || !ok {
						d.Nack(false, false)
						events <- dingo.NewEventFromError(dingo.ObjT.Consumer|dingo.ObjT.NamedConsumer, err)
					} else {
						d.Ack(false)
					}
				}()
				h, err := dingo.DecodeHeader(d.Body)
				if err != nil {
					return
				}
				tasks <- d.Body
				// block here for receipts
				reply, ok = <-receipts
				if !ok {
					return
				}

				if reply.ID != h.ID() {
					err = errors.New(fmt.Sprintf("expected: %v, received: %v", h, reply))
					return
				}
				if reply.Status == dingo.ReceiptStatus.WorkerNotFound {
					err = errors.New(fmt.Sprintf("worker not found: %v", h))
					return
				}
				return
			}()
			if !ok {
				goto clean
			}
		case _, _ = <-quit:
			goto clean
		}
	}

clean:
	err_ := ci.Channel.Cancel(tag, false)
	if err_ != nil {
		events <- dingo.NewEventFromError(dingo.ObjT.Consumer|dingo.ObjT.NamedConsumer, err_)
		// should we return here?,
		// we still need to clean the delivery channel...
	}

	// conuming remaining deliveries,
	// and don't ack them. (make them requeue in amqp)
	for cleared := false; cleared == false; {
		select {
		case d, ok := <-dv:
			if !ok {
				cleared = true
				break
			}
			// requeue
			d.Nack(false, true)
		default:
			cleared = true
		}
	}

	// close output channel
	close(tasks)

	return
}
Example #3
0
func (me *backend) _store_routine_(
	quit <-chan int,
	done chan<- int,
	events chan<- *dingo.Event,
	reports chan<- []byte,
	ci *AmqpChannel,
	dv <-chan amqp.Delivery,
	id dingo.Meta) {

	var (
		err            error
		isChannelError bool = false
	)

	defer func() {
		if isChannelError {
			// when error occurs, this channel is
			// automatically closed.
			me.receiver.ReleaseChannel(nil)
		} else {
			me.receiver.ReleaseChannel(ci)
		}
		done <- 1
		close(done)
		close(reports)
	}()

finished:
	for {
		select {
		case _, _ = <-quit:
			break finished
		case d, ok := <-dv:
			if !ok {
				break finished
			}

			d.Ack(false)
			reports <- d.Body
		}
	}

	// consuming remaining stuffs in queue
done:
	for {
		select {
		case d, ok := <-dv:
			if !ok {
				break done
			}

			d.Ack(false)
			reports <- d.Body
		default:
			break done
		}
	}

	// cancel consuming
	err = ci.Channel.Cancel(getConsumerTag(id), false)
	if err != nil {
		events <- dingo.NewEventFromError(dingo.ObjT.Store, err)
		isChannelError = true
		return
	}

	// unbind queue from exchange
	qName, rKey := getQueueName(id), getRoutingKey(id)
	err = ci.Channel.QueueUnbind(
		qName,            // name of queue
		rKey,             // routing key
		"dingo.x.result", // name of exchange
		nil,              // args
	)
	if err != nil {
		events <- dingo.NewEventFromError(dingo.ObjT.Store, err)
		isChannelError = true
		return
	}

	// delete queue
	_, err = ci.Channel.QueueDelete(
		qName, // name of queue
		true,  // ifUnused
		true,  // ifEmpty
		false, // noWait
	)
	if err != nil {
		events <- dingo.NewEventFromError(dingo.ObjT.Store, err)
		isChannelError = true
		return
	}
}
Example #4
0
func (me *backend) _reporter_routine_(quit <-chan int, done chan<- int, events chan<- *dingo.Event, reports <-chan *dingo.ReportEnvelope) {
	var (
		err error
	)
	defer func() {
		done <- 1
	}()

	conn := me.pool.Get()
	defer conn.Close()
	for {
		select {
		case _, _ = <-quit:
			goto clean
		case e, ok := <-reports:
			if !ok {
				goto clean
			}

			_, err = conn.Do("LPUSH", getKey(e.ID), e.Body)
			if err != nil {
				events <- dingo.NewEventFromError(dingo.ObjT.Reporter, err)
				break
			}
		}
	}
clean:
}

func (me *backend) _store_routine_(quit <-chan int, done chan<- int, events chan<- *dingo.Event, reports chan<- []byte, id dingo.Meta) {
	conn := me.pool.Get()
	defer func() {
		// delete key in redis
		_, err := conn.Do("DEL", getKey(id))
		if err != nil {
			events <- dingo.NewEventFromError(dingo.ObjT.Store, err)
		}

		err = conn.Close()
		if err != nil {
			events <- dingo.NewEventFromError(dingo.ObjT.Store, err)
		}

		done <- 1
	}()

finished:
	for {
		select {
		case _, _ = <-quit:
			break finished
		default:
			// blocking call to redis
			reply, err := conn.Do("BRPOP", getKey(id), me.cfg.GetPollTimeout())
			if err != nil {
				events <- dingo.NewEventFromError(dingo.ObjT.Store, err)
				break
			}
			if reply == nil {
				// timeout
				break
			}

			v, ok := reply.([]interface{})
			if !ok {
				events <- dingo.NewEventFromError(
					dingo.ObjT.Store,
					errors.New(fmt.Sprintf("Unable to get array of interface{} from %v", reply)),
				)
				break
			}
			if len(v) != 2 {
				events <- dingo.NewEventFromError(
					dingo.ObjT.Store,
					errors.New(fmt.Sprintf("length of reply is not 2, but %v", v)),
				)
				break
			}

			b, ok := v[1].([]byte)
			if !ok {
				events <- dingo.NewEventFromError(
					dingo.ObjT.Store,
					errors.New(fmt.Sprintf("the first object of reply is not byte-array, but %v", v)),
				)
				break
			}

			reports <- b
		}
	}
}

//
// private function
//

func getKey(meta dingo.Meta) string {
	return fmt.Sprintf("%v.%s.%s", _redisResultQueue, meta.Name(), meta.ID())
}
Example #5
0
func (me *backend) _reporter_routine_(quit <-chan int, done chan<- int, events chan<- *dingo.Event, reports <-chan *dingo.ReportEnvelope) {
	defer func() {
		done <- 1
	}()

	out := func(e *dingo.ReportEnvelope) (err error) {
		// report an error event when leaving
		defer func() {
			if err != nil {
				events <- dingo.NewEventFromError(dingo.ObjT.Reporter, err)
			}
		}()

		// acquire a channel
		ci, err := me.sender.Channel()
		if err != nil {
			return
		}
		defer me.sender.ReleaseChannel(ci)

		// QueueDeclare and QueueBind should be done in Poll(...)
		err = ci.Channel.Publish(
			"dingo.x.result",    // name of exchange
			getRoutingKey(e.ID), // routing key
			false,               // madatory
			false,               // immediate
			amqp.Publishing{
				DeliveryMode: amqp.Persistent,
				ContentType:  "text/json",
				Body:         e.Body,
			},
		)
		if err != nil {
			return
		}

		// block until amqp.Channel.NotifyPublish
		cf := <-ci.Confirm
		if !cf.Ack {
			err = errors.New("Unable to publish to server")
			return
		}

		return
	}

finished:
	for {
		select {
		case _, _ = <-quit:
			break finished
		case e, ok := <-reports:
			if !ok {
				// reports channel is closed
				break finished
			}
			out(e)
		}
	}

done:
	// cosume all remaining reports
	for {
		select {
		case e, ok := <-reports:
			if !ok {
				break done
			}
			out(e)
		default:
			break done
		}
	}
}