@Override
  public QueueSet getSubscriptions(
      String subscriberQueuePath, String firstSubscriptionQueuePath, int limit) {

    UUID subscriberQueueId = getQueueId(subscriberQueuePath);

    Keyspace ko = cass.getApplicationKeyspace(applicationId);

    if (firstSubscriptionQueuePath != null) {
      limit += 1;
    }

    List<HColumn<String, UUID>> columns =
        createSliceQuery(ko, ue, se, ue)
            .setKey(subscriberQueueId)
            .setColumnFamily(QUEUE_SUBSCRIPTIONS.getColumnFamily())
            .setRange(normalizeQueuePath(firstSubscriptionQueuePath), null, false, limit + 1)
            .execute()
            .get()
            .getColumns();

    QueueSet queues = new QueueSet();

    int count = Math.min(limit, columns.size());
    if (columns != null) {
      for (int i = firstSubscriptionQueuePath != null ? 1 : 0; i < count; i++) {
        HColumn<String, UUID> column = columns.get(i);
        queues.addQueue(column.getName(), column.getValue());
      }
    }
    if (columns.size() > limit) {
      queues.setMore(true);
    }
    return queues;
  }
  public void batchUpdateQueuePropertiesIndexes(
      Mutator<ByteBuffer> batch,
      String subscriberQueuePath,
      UUID subscriberQueueId,
      Map<String, Object> properties,
      UUID timestampUuid)
      throws Exception {

    QueueSet subscriptions = getSubscriptions(subscriberQueuePath, null, ALL_COUNT);

    if (subscriptions != null) {

      for (Map.Entry<String, Object> property : properties.entrySet()) {

        if (!Queue.QUEUE_PROPERTIES.containsKey(property.getKey())) {

          QueueIndexUpdate indexUpdate =
              batchStartQueueIndexUpdate(
                  batch,
                  subscriberQueuePath,
                  subscriberQueueId,
                  property.getKey(),
                  property.getValue(),
                  timestampUuid);

          for (QueueInfo subscription : subscriptions.getQueues()) {
            batchUpdateQueueIndex(indexUpdate, subscription.getUuid());
          }
        }
      }
    }
  }
  @Override
  public QueueSet unsubscribeFromQueues(
      String subscriberQueuePath, List<String> publisherQueuePaths) {

    subscriberQueuePath = normalizeQueuePath(subscriberQueuePath);
    UUID subscriberQueueId = getQueueId(subscriberQueuePath);

    UUID timestampUuid = newTimeUUID();
    long timestamp = getTimestampInMicros(timestampUuid);

    Mutator<ByteBuffer> batch = createMutator(cass.getApplicationKeyspace(applicationId), be);

    QueueSet queues = new QueueSet();

    for (String publisherQueuePath : publisherQueuePaths) {

      publisherQueuePath = normalizeQueuePath(publisherQueuePath);
      UUID publisherQueueId = getQueueId(publisherQueuePath);

      batchUnsubscribeFromQueue(
          batch,
          publisherQueuePath,
          publisherQueueId,
          subscriberQueuePath,
          subscriberQueueId,
          timestamp);

      try {
        Queue queue = getQueue(subscriberQueuePath, subscriberQueueId);

        batchUpdateQueuePropertiesIndexes(
            batch,
            publisherQueueId,
            subscriberQueuePath,
            subscriberQueueId,
            emptyMapWithKeys(queue.getProperties()),
            timestampUuid);
      } catch (Exception e) {
        logger.error("Unable to update index", e);
      }

      queues.addQueue(publisherQueuePath, publisherQueueId);
    }

    batchExecute(batch, RETRY_COUNT);

    return queues;
  }
  public QueueSet searchQueueIndex(UUID publisherQueueId, QuerySlice slice, int count)
      throws Exception {

    ByteBuffer start = null;
    if (slice.getCursor() != null) {
      start = slice.getCursor();
    } else if (slice.getStart() != null) {
      DynamicComposite s =
          new DynamicComposite(slice.getStart().getCode(), slice.getStart().getValue());
      if (!slice.getStart().isInclusive()) {
        setEqualityFlag(s, ComponentEquality.GREATER_THAN_EQUAL);
      }
      start = s.serialize();
    }

    ByteBuffer finish = null;
    if (slice.getFinish() != null) {
      DynamicComposite f =
          new DynamicComposite(slice.getFinish().getCode(), slice.getFinish().getValue());
      if (slice.getFinish().isInclusive()) {
        setEqualityFlag(f, ComponentEquality.GREATER_THAN_EQUAL);
      }
      finish = f.serialize();
    }

    if (slice.isReversed() && (start != null) && (finish != null)) {
      ByteBuffer temp = start;
      start = finish;
      finish = temp;
    }

    List<HColumn<ByteBuffer, ByteBuffer>> results =
        createSliceQuery(cass.getApplicationKeyspace(applicationId), be, be, be)
            .setColumnFamily(PROPERTY_INDEX.getColumnFamily())
            .setKey(bytebuffer(key(publisherQueueId, slice.getPropertyName())))
            .setRange(start, finish, slice.isReversed(), count)
            .execute()
            .get()
            .getColumns();

    QueueSet queues = new QueueSet();
    for (HColumn<ByteBuffer, ByteBuffer> column : results) {
      DynamicComposite c = DynamicComposite.fromByteBuffer(column.getName());
      queues.addQueue(c.get(3, se), c.get(2, ue));
    }
    return queues;
  }
  @Override
  public Message postToQueue(String queuePath, Message message) {
    long timestamp = cass.createTimestamp();
    Mutator<ByteBuffer> batch = createMutator(cass.getApplicationKeyspace(applicationId), be);

    queuePath = normalizeQueuePath(queuePath);

    MessageIndexUpdate indexUpdate = new MessageIndexUpdate(message);

    batchPostToQueue(batch, queuePath, message, indexUpdate, timestamp);

    batchExecute(batch, RETRY_COUNT);

    String firstSubscriberQueuePath = null;
    while (true) {

      QueueSet subscribers = getSubscribers(queuePath, firstSubscriberQueuePath, 1000);

      if (subscribers.getQueues().isEmpty()) {
        break;
      }

      batch = createMutator(cass.getApplicationKeyspace(applicationId), be);
      for (QueueInfo q : subscribers.getQueues()) {
        batchPostToQueue(batch, q.getPath(), message, indexUpdate, timestamp);

        firstSubscriberQueuePath = q.getPath();
      }
      batchExecute(batch, RETRY_COUNT);

      if (!subscribers.hasMore()) {
        break;
      }
    }

    return message;
  }
  @Override
  public QueueSet searchSubscribers(String publisherQueuePath, Query query) {

    if (query == null) {
      query = new Query();
    }

    publisherQueuePath = normalizeQueuePath(publisherQueuePath);
    UUID publisherQueueId = getQueueId(publisherQueuePath);

    if (!query.hasFilterPredicates() && !query.hasSortPredicates()) {

      return getSubscribers(publisherQueuePath, null, query.getLimit());
    }

    QueueSet results = null;
    String composite_cursor = null;

    QueryProcessor qp = new QueryProcessor(query);
    List<QuerySlice> slices = qp.getSlices();
    int search_count = query.getLimit() + 1;
    if (slices.size() > 1) {
      search_count = DEFAULT_SEARCH_COUNT;
    }
    for (QuerySlice slice : slices) {

      QueueSet r = null;
      try {
        r = searchQueueIndex(publisherQueueId, slice, search_count);
      } catch (Exception e) {
        logger.error("Error during search", e);
      }

      if (r == null) {
        continue;
      }

      if (r.size() > query.getLimit()) {
        r.setCursorToLastResult();
      }

      if (r.getCursor() != null) {
        if (composite_cursor != null) {
          composite_cursor += "|";
        } else {
          composite_cursor = "";
        }
        int hashCode = slice.hashCode();
        logger.info("Cursor hash code: {} ", hashCode);
        composite_cursor += hashCode + ":" + r.getCursor();
      }

      if (results != null) {
        results.and(r);
      } else {
        results = r;
      }
    }

    return results;
  }