test_class.file: oak-search-elastic/src/test/java/org/apache/jackrabbit/oak/plugins/index/elastic/ElasticIndexCleanerTest.java
test_class.identifier: ElasticIndexCleanerTest
test_class.superclass: extends ElasticAbstractQueryTest
test_case.body: @Test
      public void testIndexDeletion() throws Exception {
          String indexId1 = createIndexAndContentNode("propa", "test1");
          String indexId2 = createIndexAndContentNode("propb", "test2");
          String indexId3 = createIndexAndContentNode("propc", "test3");
          String indexPath1 = "/" + INDEX_DEFINITIONS_NAME + "/" + indexId1;
          String indexPath2 = "/" + INDEX_DEFINITIONS_NAME + "/" + indexId2;
          String indexPath3 = "/" + INDEX_DEFINITIONS_NAME + "/" + indexId3;
          NodeState oakIndex = nodeStore.getRoot().getChildNode(INDEX_DEFINITIONS_NAME);
          NodeState indexState1 = oakIndex.getChildNode(indexId1);
          NodeState indexState2 = oakIndex.getChildNode(indexId2);
          NodeState indexState3 = oakIndex.getChildNode(indexId3);
  
          root.refresh();
          root.getTree(indexPath1).remove();
          root.getTree(indexPath2).remove();
          root.commit();
  
          oakIndex = nodeStore.getRoot().getChildNode(INDEX_DEFINITIONS_NAME);
          assertFalse(oakIndex.getChildNode(indexId1).exists());
          assertFalse(oakIndex.getChildNode(indexId2).exists());
  
          ElasticIndexCleaner cleaner = new ElasticIndexCleaner(esConnection, nodeStore, 5);
          cleaner.run();
  
          String remoteIndexName1 = ElasticIndexNameHelper.getRemoteIndexName(esConnection.getIndexPrefix(), indexState1,
                  indexPath1);
          String remoteIndexName2 = ElasticIndexNameHelper.getRemoteIndexName(esConnection.getIndexPrefix(), indexState2,
                  indexPath2);
          String remoteIndexName3 = ElasticIndexNameHelper.getRemoteIndexName(esConnection.getIndexPrefix(), indexState3,
                  indexPath3);
  
          assertTrue(esConnection.getClient().indices().exists(new GetIndexRequest(remoteIndexName1), RequestOptions.DEFAULT));
          assertTrue(esConnection.getClient().indices().exists(new GetIndexRequest(remoteIndexName2), RequestOptions.DEFAULT));
          assertTrue(esConnection.getClient().indices().exists(new GetIndexRequest(remoteIndexName3), RequestOptions.DEFAULT));
  
          Thread.sleep(5000);
          cleaner.run();
  
          assertFalse(esConnection.getClient().indices().exists(new GetIndexRequest(remoteIndexName1), RequestOptions.DEFAULT));
          assertFalse(esConnection.getClient().indices().exists(new GetIndexRequest(remoteIndexName2), RequestOptions.DEFAULT));
          assertTrue(esConnection.getClient().indices().exists(new GetIndexRequest(remoteIndexName3), RequestOptions.DEFAULT));
      }
test_case.class_method_signature: ElasticIndexCleanerTest.testIndexDeletion()
test_case.full_signature: @Test public void testIndexDeletion()
test_case.identifier: testIndexDeletion
test_case.invocations: createIndexAndContentNode createIndexAndContentNode createIndexAndContentNode getChildNode getRoot getChildNode getChildNode getChildNode refresh remove getTree remove getTree commit getChildNode getRoot assertFalse exists getChildNode assertFalse exists getChildNode run getRemoteIndexName getIndexPrefix getRemoteIndexName getIndexPrefix getRemoteIndexName getIndexPrefix assertTrue exists indices getClient assertTrue exists indices getClient assertTrue exists indices getClient sleep run assertFalse exists indices getClient assertFalse exists indices getClient assertTrue exists indices getClient
test_case.modifiers: @Test public
test_case.parameters: ()
test_case.return: void
test_case.signature: void testIndexDeletion()
focal_class.declarator: LOG = LoggerFactory.getLogger(ElasticIndexCleaner.class)
focal_class.modifier: private static final
focal_class.original_string: private static final Logger LOG = LoggerFactory.getLogger(ElasticIndexCleaner.class);
focal_class.type: Logger
focal_class.var_name: LOG
focal_class.declarator: elasticConnection
focal_class.modifier: private final
focal_class.original_string: private final ElasticConnection elasticConnection;
focal_class.type: ElasticConnection
focal_class.var_name: elasticConnection
focal_class.declarator: nodeStore
focal_class.modifier: private final
focal_class.original_string: private final NodeStore nodeStore;
focal_class.type: NodeStore
focal_class.var_name: nodeStore
focal_class.declarator: indexPrefix
focal_class.modifier: private final
focal_class.original_string: private final String indexPrefix;
focal_class.type: String
focal_class.var_name: indexPrefix
focal_class.declarator: danglingRemoteIndicesMap
focal_class.modifier: private final
focal_class.original_string: private final Map<String, Long> danglingRemoteIndicesMap;
focal_class.type: Map<String, Long>
focal_class.var_name: danglingRemoteIndicesMap
focal_class.declarator: threshold
focal_class.modifier: private final
focal_class.original_string: private final int threshold;
focal_class.type: int
focal_class.var_name: threshold
focal_class.file: oak-search-elastic/src/main/java/org/apache/jackrabbit/oak/plugins/index/elastic/ElasticIndexCleaner.java
focal_class.identifier: ElasticIndexCleaner
focal_class.interfaces: implements Runnable
focal_class.class_method_signature: ElasticIndexCleaner.ElasticIndexCleaner(ElasticConnection elasticConnection, NodeStore nodeStore, int thresholdInSeconds)
focal_class.full_signature: public  ElasticIndexCleaner(ElasticConnection elasticConnection, NodeStore nodeStore, int thresholdInSeconds)
focal_class.identifier: ElasticIndexCleaner
focal_class.modifiers: public
focal_class.parameters: (ElasticConnection elasticConnection, NodeStore nodeStore, int thresholdInSeconds)
focal_class.signature:  ElasticIndexCleaner(ElasticConnection elasticConnection, NodeStore nodeStore, int thresholdInSeconds)
focal_class.class_method_signature: ElasticIndexCleaner.run()
focal_class.full_signature: public void run()
focal_class.identifier: run
focal_class.modifiers: public
focal_class.parameters: ()
focal_class.return: void
focal_class.signature: void run()
focal_method.body: public void run() {
          try {
              NodeState root = nodeStore.getRoot();
              GetIndexRequest getIndexRequest = new GetIndexRequest(elasticConnection.getIndexPrefix() + "*")
                      .indicesOptions(IndicesOptions.lenientExpandOpen());
              String[] remoteIndices = elasticConnection.getClient().indices()
                      .get(getIndexRequest, RequestOptions.DEFAULT).getIndices();
              if (remoteIndices == null || remoteIndices.length == 0) {
                  LOG.debug("No remote index found with prefix {}", indexPrefix);
                  return;
              }
              // remove entry of remote index names which don't exist now
              List<String> externallyDeletedIndices = danglingRemoteIndicesMap.keySet().stream().
                      filter(index -> Arrays.stream(remoteIndices).noneMatch(remoteIndex -> remoteIndex.equals(index))).collect(Collectors.toList());
              externallyDeletedIndices.forEach(danglingRemoteIndicesMap::remove);
              Set<String> existingIndices = new HashSet<>();
              root.getChildNode(INDEX_DEFINITIONS_NAME).getChildNodeEntries().forEach(childNodeEntry -> {
                  PropertyState typeProperty = childNodeEntry.getNodeState().getProperty(IndexConstants.TYPE_PROPERTY_NAME);
                  String typeValue = typeProperty != null ? typeProperty.getValue(Type.STRING) : "";
                  /*
                  If index type is "elasticsearch" or "disabled", we try to find remote index name. In case of disabled lucene or
                  property indices, the remote index name would be null. So only elasticsearch indices are affected here.
                   */
                  if (typeValue.equals(ElasticIndexDefinition.TYPE_ELASTICSEARCH) || typeValue.equals("disabled")) {
                      String indexPath = "/" + INDEX_DEFINITIONS_NAME + "/" + childNodeEntry.getName();
                      String remoteIndexName = ElasticIndexNameHelper.getRemoteIndexName(indexPrefix, childNodeEntry.getNodeState(), indexPath);
                      if (remoteIndexName != null) {
                          existingIndices.add(remoteIndexName);
                      }
                  }
              });
  
              List<String> indicesToDelete = new ArrayList<>();
              for (String remoteIndexName : remoteIndices) {
                  if (!existingIndices.contains(remoteIndexName)) {
                      Long curTime = System.currentTimeMillis();
                      Long oldTime = danglingRemoteIndicesMap.putIfAbsent(remoteIndexName, curTime);
                      if (threshold == 0 || (oldTime != null && curTime - oldTime >= TimeUnit.SECONDS.toMillis(threshold))) {
                          indicesToDelete.add(remoteIndexName);
                          danglingRemoteIndicesMap.remove(remoteIndexName);
                      }
                  } else {
                      danglingRemoteIndicesMap.remove(remoteIndexName);
                  }
              }
              DeleteIndexRequest deleteIndexRequest = new DeleteIndexRequest(indicesToDelete.toArray(new String[]{}));
              if (deleteIndexRequest.indices() != null && deleteIndexRequest.indices().length > 0) {
                  String indexString = Arrays.toString(deleteIndexRequest.indices());
                  AcknowledgedResponse acknowledgedResponse = elasticConnection.getClient().indices().delete(deleteIndexRequest, RequestOptions.DEFAULT);
                  LOG.info("Deleting remote indices {}", indexString);
                  if (!acknowledgedResponse.isAcknowledged()) {
                      LOG.error("Could not delete remote indices " + indexString);
                  }
              }
          } catch (IOException e) {
              LOG.error("Could not delete remote indices", e);
          }
      }
focal_method.class_method_signature: ElasticIndexCleaner.run()
focal_method.full_signature: public void run()
focal_method.identifier: run
focal_method.invocations: getRoot indicesOptions getIndexPrefix lenientExpandOpen getIndices get indices getClient debug collect filter stream keySet noneMatch stream equals toList forEach forEach getChildNodeEntries getChildNode getProperty getNodeState getValue equals equals getName getRemoteIndexName getNodeState add contains currentTimeMillis putIfAbsent toMillis add remove remove toArray indices indices toString indices delete indices getClient info isAcknowledged error error
focal_method.modifiers: public
focal_method.parameters: ()
focal_method.return: void
focal_method.signature: void run()