// zpop pops a value from the ZSET key using WATCH/MULTI/EXEC commands. func zpop(c redis.Conn, key string) (result string, err error) { defer func() { // Return connection to normal state on error. if err != nil { c.Do("DISCARD") } }() // Loop until transaction is successful. for { if _, err := c.Do("WATCH", key); err != nil { return "", err } members, err := redis.Strings(c.Do("ZRANGE", key, 0, 0)) if err != nil { return "", err } if len(members) != 1 { return "", redis.ErrNil } c.Send("MULTI") c.Send("ZREM", key, members[0]) queued, err := c.Do("EXEC") if err != nil { return "", err } if queued != nil { result = members[0] break } } return result, nil }
func (m *baseModel) delete(id string, conn redis.Conn) error { m.log.Debugf("Deleting %s %s", m.idType, id) existing := reflect.New(m.objType).Interface() existingErr := m.fetch(id, existing, false, conn) if existingErr != nil && existingErr != RecordNotFound { return fmt.Errorf("Failed fetching existing %s before delete. error:%s", m.idType, existingErr) } if existingErr == RecordNotFound { lastUpdated, _ := m.getLastUpdated(id, conn) if lastUpdated == nil { // Hasn't ever existed... return existingErr } // At this point we may have a RecordNotFound, but we may as well delete again anyway, just in case m.log.Infof("%s id:%s appears to be already deleted, but we'll try again anyway.", m.idType, id) } defer syncFS() conn.Send("MULTI") conn.Send("SREM", m.idType+"s", id) conn.Send("DEL", m.idType+":"+id) _, err := conn.Do("EXEC") if m.afterDelete != nil && existingErr == nil { err = m.afterDelete(existing, conn) if err != nil { return fmt.Errorf("Failed on afterDelete: %s", err) } } if m.sendEvent != nil { m.sendEvent("deleted", id) } return m.markUpdated(id, time.Now(), conn) }