Esempio n. 1
0
File: pubsub.go Progetto: mc0/okq
// subManager is responsible for adding/removing channel subscriptions in redis.
// We do all this craziness instead of just doing a simple psubscribe so that if
// we have a bunch of okq instances, this one will only handle the publishes for
// the channels that the consumers connected to it actually care about. So we
// could partition consumer group A too hit one okq instance and group B hit
// another, and those two instances won't be stuck processing publishes that
// they don't care about
func subManager(subConn *pubsubch.PubSubCh, updateCh chan struct{}) {
	lastSubscribedQueues := []string{}
	tick := time.Tick(30 * time.Second)
	// ensure we run immediately by filling the channel
	select {
	case updateCh <- struct{}{}:
	default:
	}

outer:
	for {
		select {
		case <-tick:
			if err := subConn.Ping(); err != nil {
				log.L.Printf("notifyConsumers error pinging: %v", err)
				break outer
			}
		case _, ok := <-updateCh:
			if !ok {
				break outer
			}

			queueNames := registeredQueues()
			queuesAdded := stringSliceSub(queueNames, lastSubscribedQueues)
			queuesRemoved := stringSliceSub(lastSubscribedQueues, queueNames)

			lastSubscribedQueues = queueNames

			if len(queuesRemoved) != 0 {
				var redisChannels []string
				for i := range queuesRemoved {
					channelName := db.QueueChannelNameKey(queuesRemoved[i])
					redisChannels = append(redisChannels, channelName)
				}

				log.L.Debugf("unsubscribing from %v", redisChannels)
				if _, err := subConn.Unsubscribe(redisChannels...); err != nil {
					log.L.Printf("notifyConsumers error unsubscribing: %v", err)
					break outer
				}
			}

			if len(queuesAdded) != 0 {
				var redisChannels []string
				for i := range queuesAdded {
					channelName := db.QueueChannelNameKey(queuesAdded[i])
					redisChannels = append(redisChannels, channelName)
				}

				log.L.Debugf("subscribing to %v", redisChannels)
				if _, err := subConn.Subscribe(redisChannels...); err != nil {
					log.L.Printf("notifyConsumers error subscribing: %v", err)
					break outer
				}
			}
		}
	}

	subConn.Close()
}
Esempio n. 2
0
File: commands.go Progetto: mc0/okq
func qpushgeneric(
	_ *clients.Client, args []string, pushRight bool,
) (
	interface{}, error,
) {
	queueName, eventID, contents := args[0], args[1], args[2]
	restArgs := args[3:]
	if len(restArgs) > 0 && isEqualUpper("NOBLOCK", restArgs[0]) {
		coreArgs := args[:3]
		select {
		case qpushBGCh <- &qpushBG{coreArgs, pushRight}:
			return okSS, nil
		default:
			return fmt.Errorf("ERR too busy to process NOBLOCK event"), nil
		}
	}

	itemsKey := db.ItemsKey(queueName)

	created, err := db.Inst.Cmd("HSETNX", itemsKey, eventID, contents).Int()
	if err != nil {
		return nil, fmt.Errorf("QPUSH* HSETNX: %s", err)
	} else if created == 0 {
		return fmt.Errorf("ERR duplicate event %s", eventID), nil
	}

	unclaimedKey := db.UnclaimedKey(queueName)
	cmd := "LPUSH"
	if pushRight {
		cmd = "RPUSH"
	}
	channelName := db.QueueChannelNameKey(queueName)

	_, err = db.Inst.Pipe(
		db.PP(cmd, unclaimedKey, eventID),
		db.PP("PUBLISH", channelName, eventID),
	)
	if err != nil {
		return nil, fmt.Errorf("QPUSH* %s/PUBLISH: %s", cmd, err)
	}

	return okSS, nil
}