private void insertOrUpdateSubsAndIndexes(final CNSSubscription subscription, Integer ttl)
     throws Exception {
   subscription.checkIsValid();
   CmbComposite columnName =
       cassandraHandler.getCmbComposite(
           subscription.getEndpoint(), subscription.getProtocol().name());
   cassandraHandler.update(
       AbstractDurablePersistence.CNS_KEYSPACE,
       columnFamilySubscriptions,
       subscription.getTopicArn(),
       columnName,
       getColumnValuesJSON(subscription),
       CMB_SERIALIZER.STRING_SERIALIZER,
       CMB_SERIALIZER.COMPOSITE_SERIALIZER,
       CMB_SERIALIZER.STRING_SERIALIZER,
       ttl);
   cassandraHandler.insertRow(
       AbstractDurablePersistence.CNS_KEYSPACE,
       subscription.getArn(),
       columnFamilySubscriptionsIndex,
       getIndexColumnValues(subscription.getEndpoint(), subscription.getProtocol()),
       CMB_SERIALIZER.STRING_SERIALIZER,
       CMB_SERIALIZER.STRING_SERIALIZER,
       CMB_SERIALIZER.STRING_SERIALIZER,
       ttl);
   cassandraHandler.insertRow(
       AbstractDurablePersistence.CNS_KEYSPACE,
       subscription.getUserId(),
       columnFamilySubscriptionsUserIndex,
       new HashMap<String, String>() {
         {
           put(subscription.getArn(), "");
         }
       },
       CMB_SERIALIZER.STRING_SERIALIZER,
       CMB_SERIALIZER.STRING_SERIALIZER,
       CMB_SERIALIZER.STRING_SERIALIZER,
       ttl);
   cassandraHandler.insertRow(
       AbstractDurablePersistence.CNS_KEYSPACE,
       subscription.getToken(),
       columnFamilySubscriptionsTokenIndex,
       new HashMap<String, String>() {
         {
           put(subscription.getArn(), "");
         }
       },
       CMB_SERIALIZER.STRING_SERIALIZER,
       CMB_SERIALIZER.STRING_SERIALIZER,
       CMB_SERIALIZER.STRING_SERIALIZER,
       ttl);
 }
  @Override
  public void unsubscribe(String arn) throws Exception {

    CNSSubscription s = getSubscription(arn);

    if (s != null) {

      deleteIndexes(arn, s.getUserId(), s.getToken());
      CmbComposite columnName =
          cassandraHandler.getCmbComposite(s.getEndpoint(), s.getProtocol().name());
      cassandraHandler.delete(
          AbstractDurablePersistence.CNS_KEYSPACE,
          columnFamilySubscriptions,
          Util.getCnsTopicArn(arn),
          columnName,
          CMB_SERIALIZER.STRING_SERIALIZER,
          CMB_SERIALIZER.COMPOSITE_SERIALIZER);

      if (s.isConfirmed()) {
        cassandraHandler.decrementCounter(
            AbstractDurablePersistence.CNS_KEYSPACE,
            columnFamilyTopicStats,
            s.getTopicArn(),
            "subscriptionConfirmed",
            1,
            CMB_SERIALIZER.STRING_SERIALIZER,
            CMB_SERIALIZER.STRING_SERIALIZER);
      } else {
        cassandraHandler.decrementCounter(
            AbstractDurablePersistence.CNS_KEYSPACE,
            columnFamilyTopicStats,
            s.getTopicArn(),
            "subscriptionPending",
            1,
            CMB_SERIALIZER.STRING_SERIALIZER,
            CMB_SERIALIZER.STRING_SERIALIZER);
      }

      cassandraHandler.incrementCounter(
          AbstractDurablePersistence.CNS_KEYSPACE,
          columnFamilyTopicStats,
          s.getTopicArn(),
          "subscriptionDeleted",
          1,
          CMB_SERIALIZER.STRING_SERIALIZER,
          CMB_SERIALIZER.STRING_SERIALIZER);
    }
  }
  @Override
  public CNSSubscription getSubscription(String arn) throws Exception {

    // read form index to get composite col-name

    CmbColumnSlice<String, String> slice =
        cassandraHandler.readColumnSlice(
            AbstractDurablePersistence.CNS_KEYSPACE,
            columnFamilySubscriptionsIndex,
            arn,
            null,
            null,
            1,
            CMB_SERIALIZER.STRING_SERIALIZER,
            CMB_SERIALIZER.STRING_SERIALIZER,
            CMB_SERIALIZER.STRING_SERIALIZER);

    if (slice != null) {

      // get Column from main table

      String colName = slice.getColumns().get(0).getName();
      CnsSubscriptionProtocol protocol = getEndpointAndProtoIndexValProtocol(colName);
      String endpoint = getEndpointAndProtoIndexValEndpoint(colName);
      CmbComposite columnName = cassandraHandler.getCmbComposite(endpoint, protocol.name());
      CmbColumn<CmbComposite, String> column =
          cassandraHandler.readColumn(
              AbstractDurablePersistence.CNS_KEYSPACE,
              columnFamilySubscriptions,
              Util.getCnsTopicArn(arn),
              columnName,
              CMB_SERIALIZER.STRING_SERIALIZER,
              CMB_SERIALIZER.COMPOSITE_SERIALIZER,
              CMB_SERIALIZER.STRING_SERIALIZER);

      if (column != null) {
        CNSSubscription s = extractSubscriptionFromColumn(column, Util.getCnsTopicArn(arn));
        s.checkIsValid();
        return s;
      }
    }

    return null;
  }
  /**
   * Enumerate all subs in a topic
   *
   * @param nextToken The ARN of the last sub-returned or null is first time call.
   * @param topicArn
   * @param protocol
   * @param pageSize
   * @param hidePendingArn
   * @return The list of subscriptions given a topic. Note: if nextToken is provided, the returned
   *     list will not contain it for convenience
   * @throws Exception
   */
  public List<CNSSubscription> listSubscriptionsByTopic(
      String nextToken,
      String topicArn,
      CnsSubscriptionProtocol protocol,
      int pageSize,
      boolean hidePendingArn)
      throws Exception {

    if (nextToken != null) {
      if (getSubscription(nextToken) == null) {
        throw new SubscriberNotFoundException("Subscriber not found for arn " + nextToken);
      }
    }

    // read from index to get composite-col-name corresponding to nextToken
    CmbComposite nextTokenComposite = null;

    if (nextToken != null) {
      CmbColumnSlice<String, String> slice =
          cassandraHandler.readColumnSlice(
              AbstractDurablePersistence.CNS_KEYSPACE,
              columnFamilySubscriptionsIndex,
              nextToken,
              null,
              null,
              1,
              CMB_SERIALIZER.STRING_SERIALIZER,
              CMB_SERIALIZER.STRING_SERIALIZER,
              CMB_SERIALIZER.STRING_SERIALIZER);
      if (slice == null) {
        throw new IllegalArgumentException("Could not find any subscription with arn " + nextToken);
      }
      // get Column from main table
      String colName = slice.getColumns().get(0).getName();
      CnsSubscriptionProtocol tokProtocol = getEndpointAndProtoIndexValProtocol(colName);
      String endpoint = getEndpointAndProtoIndexValEndpoint(colName);
      nextTokenComposite = cassandraHandler.getCmbComposite(endpoint, tokProtocol.name());
    }

    List<CNSSubscription> l = new ArrayList<CNSSubscription>();

    CNSTopic t = PersistenceFactory.getTopicPersistence().getTopic(topicArn);

    if (t == null) {
      throw new TopicNotFoundException("Resource not found.");
    }

    // read pageSize at a time

    CmbColumnSlice<CmbComposite, String> cols =
        cassandraHandler.readColumnSlice(
            AbstractDurablePersistence.CNS_KEYSPACE,
            columnFamilySubscriptions,
            topicArn,
            nextTokenComposite,
            null,
            pageSize,
            CMB_SERIALIZER.STRING_SERIALIZER,
            CMB_SERIALIZER.COMPOSITE_SERIALIZER,
            CMB_SERIALIZER.STRING_SERIALIZER);

    if (nextToken != null && cols.size() > 0) {
      cols.getColumns().remove(0);
    }

    while (l.size() < pageSize) {

      if (cols == null || cols.size() == 0) {
        return l;
      }

      for (CmbColumn<CmbComposite, String> col : cols.getColumns()) {

        CNSSubscription sub = extractSubscriptionFromColumn(col, topicArn);

        // ignore invalid subscriptions coming from Cassandra

        try {
          sub.checkIsValid();
        } catch (CMBException ex) {
          logger.error("event=invalid_subscription " + sub.toString(), ex);
          continue;
        }

        if (protocol != null && protocol != sub.getProtocol()) {
          continue;
        }

        if (hidePendingArn) {
          if (sub.isConfirmed()) {
            l.add(sub);
          } else {
            sub.setArn("PendingConfirmation");
            l.add(sub);
          }
        } else {
          l.add(sub);
        }

        if (l.size() == pageSize) {
          return l;
        }
      }

      nextTokenComposite = cols.getColumns().get(cols.size() - 1).getName();
      cols =
          cassandraHandler.readColumnSlice(
              AbstractDurablePersistence.CNS_KEYSPACE,
              columnFamilySubscriptions,
              topicArn,
              nextTokenComposite,
              null,
              pageSize,
              CMB_SERIALIZER.STRING_SERIALIZER,
              CMB_SERIALIZER.COMPOSITE_SERIALIZER,
              CMB_SERIALIZER.STRING_SERIALIZER);

      if (cols.size() > 0) {
        cols.getColumns().remove(0);
      }
    }

    return l;
  }