// 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() }
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 }