// UpdateQueues should be called whenever a client changes what queues it's // registered for. The new full list of registered queues should be passed in, // this method will do a diff and figure it out what was removed func UpdateQueues(client *clients.Client, queues []string) error { respCh := make(chan error) callCh <- func(s *state) { oldQueues := s.clientQueues(client.ID) removed := stringSliceSub(oldQueues, queues) s.addClientQueues(client, queues) s.removeClientQueues(client, removed) select { case updateNotifyCh <- struct{}{}: default: } ts := time.Now().Unix() pipe := make([]*db.PipePart, 0, len(removed)+len(queues)) for _, queueName := range removed { consumersKey := db.ConsumersKey(queueName) pipe = append(pipe, db.PP("ZREM", consumersKey, client.ID)) } for _, queueName := range queues { consumersKey := db.ConsumersKey(queueName) pipe = append(pipe, db.PP("ZADD", consumersKey, ts, client.ID)) } _, err := db.Inst.Pipe(pipe...) respCh <- err } return <-respCh }
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 }
func qack(client *clients.Client, args []string) (interface{}, error) { queueName, eventID := args[0], args[1] claimedKey := db.ClaimedKey(queueName) var numRemoved int numRemoved, err := db.Inst.Cmd("LREM", claimedKey, -1, eventID).Int() if err != nil { return nil, fmt.Errorf("QACK LREM (claimed): %s", err) } // If we didn't removed the eventID from the claimed events we see if it can // be found in unclaimed. We only do this in the uncommon case that the // eventID isn't in claimed since unclaimed can be really large, so LREM is // slow on it if numRemoved == 0 { unclaimedKey := db.UnclaimedKey(queueName) var err error numRemoved, err = db.Inst.Cmd("LREM", unclaimedKey, -1, eventID).Int() if err != nil { return nil, fmt.Errorf("QACK LREM (unclaimed): %s", err) } } // We only remove the object data itself if the eventID was actually removed // from something if numRemoved > 0 { itemsKey := db.ItemsKey(queueName) lockKey := db.ItemLockKey(queueName, eventID) _, err := db.Inst.Pipe( db.PP("HDEL", itemsKey, eventID), db.PP("DEL", lockKey), ) if err != nil { return nil, fmt.Errorf("QACK HDEL/DEL: %s", err) } } return numRemoved, nil }