private void handleIndexOperation(final ElasticsearchIndexEvent elasticsearchIndexEvent)
      throws IndexDocNotFoundException {

    Preconditions.checkNotNull(elasticsearchIndexEvent, "elasticsearchIndexEvent cannot be null");

    final UUID messageId = elasticsearchIndexEvent.getIndexBatchId();
    Preconditions.checkNotNull(messageId, "messageId must not be null");

    final String message = esMapPersistence.getString(messageId.toString());

    final IndexOperationMessage indexOperationMessage;
    if (message == null) {

      // provide some time back pressure before performing a quorum read
      if (queueFig.getQuorumFallback()
          && System.currentTimeMillis()
              > elasticsearchIndexEvent.getCreationTime() + queueFig.getLocalQuorumTimeout()) {

        if (logger.isDebugEnabled()) {
          logger.debug("ES batch with id {} not found, reading with strong consistency", messageId);
        }

        final String highConsistency =
            esMapPersistence.getStringHighConsistency(messageId.toString());
        if (highConsistency == null) {

          throw new RuntimeException(
              "ES batch with id " + messageId + " not found when reading with strong consistency");
        }

        indexOperationMessage =
            ObjectJsonSerializer.INSTANCE.fromString(highConsistency, IndexOperationMessage.class);

      } else {

        throw new IndexDocNotFoundException(elasticsearchIndexEvent.getIndexBatchId());
      }

    } else {

      indexOperationMessage =
          ObjectJsonSerializer.INSTANCE.fromString(message, IndexOperationMessage.class);
    }

    // don't let this continue if there's nothing to index
    if (indexOperationMessage == null || indexOperationMessage.isEmpty()) {
      throw new RuntimeException(
          "IndexOperationMessage cannot be null or empty after retrieving from map persistence");
    }

    // always do a check to ensure the indexes are initialized for the index requests
    initializeEntityIndexes(indexOperationMessage);

    // send it to to be indexed
    indexProducer.put(indexOperationMessage).toBlocking().last();
  }
  /**
   * Queue up an indexOperationMessage for multi region execution
   *
   * @param indexOperationMessage
   */
  public void queueIndexOperationMessage(final IndexOperationMessage indexOperationMessage) {

    // don't try to produce something with nothing
    if (indexOperationMessage == null || indexOperationMessage.isEmpty()) {
      return;
    }

    final String jsonValue = ObjectJsonSerializer.INSTANCE.toString(indexOperationMessage);

    final UUID newMessageId = UUIDGenerator.newTimeUUID();

    final int expirationTimeInSeconds =
        (int) TimeUnit.MILLISECONDS.toSeconds(indexProcessorFig.getIndexMessageTtl());

    // write to the map in ES
    esMapPersistence.putString(newMessageId.toString(), jsonValue, expirationTimeInSeconds);

    // now queue up the index message

    final ElasticsearchIndexEvent elasticsearchIndexEvent =
        new ElasticsearchIndexEvent(queueFig.getPrimaryRegion(), newMessageId);

    // send to the topic so all regions index the batch

    offerTopic(elasticsearchIndexEvent);
  }
  @Override
  public void queueDeleteEdge(final ApplicationScope applicationScope, final Edge edge) {

    // sent in region (not offerTopic) as the delete IO happens in-region, then queues a
    // multi-region de-index op
    offer(new EdgeDeleteEvent(queueFig.getPrimaryRegion(), applicationScope, edge));
  }
  @Override
  public void queueNewEdge(
      final ApplicationScope applicationScope, final Entity entity, final Edge newEdge) {

    offer(
        new EdgeIndexEvent(queueFig.getPrimaryRegion(), applicationScope, entity.getId(), newEdge));
  }
  @Override
  public void queueEntityDelete(final ApplicationScope applicationScope, final Id entityId) {

    // sent in region (not offerTopic) as the delete IO happens in-region, then queues a
    // multi-region de-index op
    offer(
        new EntityDeleteEvent(
            queueFig.getPrimaryRegion(), new EntityIdScope(applicationScope, entityId)));
  }
 @Override
 public void queueInitializeApplicationIndex(final ApplicationScope applicationScope) {
   IndexLocationStrategy indexLocationStrategy =
       indexLocationStrategyFactory.getIndexLocationStrategy(applicationScope);
   offerTopic(
       new InitializeApplicationIndexEvent(
           queueFig.getPrimaryRegion(),
           new ReplicatedIndexLocationStrategy(indexLocationStrategy)));
 }
  @Override
  public void queueDeIndexOldVersion(
      final ApplicationScope applicationScope, final Id entityId, UUID markedVersion) {

    // queue the de-index of old versions to the topic so cleanup happens in all regions
    offerTopic(
        new DeIndexOldVersionsEvent(
            queueFig.getPrimaryRegion(),
            new EntityIdScope(applicationScope, entityId),
            markedVersion));
  }
  @Override
  public void queueEntityIndexUpdate(
      final ApplicationScope applicationScope, final Entity entity, long updatedAfter) {

    offer(
        new EntityIndexEvent(
            queueFig.getPrimaryRegion(), new EntityIdScope(applicationScope, entity.getId()), 0));

    final EntityIndexOperation entityIndexOperation =
        new EntityIndexOperation(applicationScope, entity.getId(), updatedAfter);

    final IndexOperationMessage indexMessage =
        eventBuilder.buildEntityIndex(entityIndexOperation).toBlocking().lastOrDefault(null);

    queueIndexOperationMessage(indexMessage);
  }