You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

ViewIndexer.java 6.5KB

SONAR-12686 upgrade es client to 7.9.3 and move to HTTP - add should minimum match eq 1 to user index queries ES 7.X changed behaviour in case filter query with bool it defaults to '0' https://www.elastic.co/guide/en/elasticsearch/reference/7.x/breaking-changes-7.0.html#_the_filter_context_has_been_removed - fix issue index routing param ES 7.X helped discover this bug as new setting has been auto configured which is 'index.number_of_routing_shards'. This has changed how documents are distributed across shards depending on how many shards the index has. Without that change issues docs has been incorrectly routed to the same shard hash as projects and it worked no matter what routing key you used projectUuid or auth_projectUuid. - update ngram and edge_ngram names to match with es 7.x nGram and edgeNgram has been deprecated in favour of ngram and edge_ngram https://www.elastic.co/guide/en/elasticsearch/reference/current/breaking-changes-7.0.html#deprecated-ngram-edgengram-token-filter-cannot-be-used - remove `_all : enabled` usage from UT This field was already deprecated in 6.X, now it has been removed. https://www.elastic.co/guide/en/elasticsearch/reference/current/breaking-changes-7.0.html#all-meta-field-removed - add Elasticsearch High Level REST client dependency - use sonar.search.port for ES HTTP - main process use ES Rest client to check ES status - sonar.cluster.search.hosts has HTTP ports on APP nodes also sonar.search.port and sonar.search.host MUST be configured on each Search node with the host and HTTP port of the current node - use Elasticsearch high level rest client - use in EsTester - use as primary es client - use indices api to get all indices name instead of cluster api - use cluster health api to check cluster state - support raw requests for 'nodes/_stats' and '_cluster/stats' - support raw requests for 'indices/_stats' - leave netty4plugin as testCompile dependency it is used in UTs - all ES non-test calls go through EsClient class - add rest client ES profiling
4 years ago
SONAR-12686 upgrade es client to 7.9.3 and move to HTTP - add should minimum match eq 1 to user index queries ES 7.X changed behaviour in case filter query with bool it defaults to '0' https://www.elastic.co/guide/en/elasticsearch/reference/7.x/breaking-changes-7.0.html#_the_filter_context_has_been_removed - fix issue index routing param ES 7.X helped discover this bug as new setting has been auto configured which is 'index.number_of_routing_shards'. This has changed how documents are distributed across shards depending on how many shards the index has. Without that change issues docs has been incorrectly routed to the same shard hash as projects and it worked no matter what routing key you used projectUuid or auth_projectUuid. - update ngram and edge_ngram names to match with es 7.x nGram and edgeNgram has been deprecated in favour of ngram and edge_ngram https://www.elastic.co/guide/en/elasticsearch/reference/current/breaking-changes-7.0.html#deprecated-ngram-edgengram-token-filter-cannot-be-used - remove `_all : enabled` usage from UT This field was already deprecated in 6.X, now it has been removed. https://www.elastic.co/guide/en/elasticsearch/reference/current/breaking-changes-7.0.html#all-meta-field-removed - add Elasticsearch High Level REST client dependency - use sonar.search.port for ES HTTP - main process use ES Rest client to check ES status - sonar.cluster.search.hosts has HTTP ports on APP nodes also sonar.search.port and sonar.search.host MUST be configured on each Search node with the host and HTTP port of the current node - use Elasticsearch high level rest client - use in EsTester - use as primary es client - use indices api to get all indices name instead of cluster api - use cluster health api to check cluster state - support raw requests for 'nodes/_stats' and '_cluster/stats' - support raw requests for 'indices/_stats' - leave netty4plugin as testCompile dependency it is used in UTs - all ES non-test calls go through EsClient class - add rest client ES profiling
4 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. /*
  2. * SonarQube
  3. * Copyright (C) 2009-2023 SonarSource SA
  4. * mailto:info AT sonarsource DOT com
  5. *
  6. * This program is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU Lesser General Public
  8. * License as published by the Free Software Foundation; either
  9. * version 3 of the License, or (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  14. * Lesser General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU Lesser General Public License
  17. * along with this program; if not, write to the Free Software Foundation,
  18. * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  19. */
  20. package org.sonar.server.view.index;
  21. import java.util.Collection;
  22. import java.util.HashMap;
  23. import java.util.List;
  24. import java.util.Map;
  25. import java.util.Set;
  26. import java.util.stream.Collectors;
  27. import org.elasticsearch.action.admin.indices.cache.clear.ClearIndicesCacheRequest;
  28. import org.elasticsearch.action.index.IndexRequest;
  29. import org.sonar.db.DbClient;
  30. import org.sonar.db.DbSession;
  31. import org.sonar.db.component.ComponentDto;
  32. import org.sonar.db.component.UuidWithBranchUuidDto;
  33. import org.sonar.db.es.EsQueueDto;
  34. import org.sonar.server.es.BulkIndexer;
  35. import org.sonar.server.es.BulkIndexer.Size;
  36. import org.sonar.server.es.EsClient;
  37. import org.sonar.server.es.IndexType;
  38. import org.sonar.server.es.IndexingListener;
  39. import org.sonar.server.es.IndexingResult;
  40. import org.sonar.server.es.OneToOneResilientIndexingListener;
  41. import org.sonar.server.es.ResilientIndexer;
  42. import static org.sonar.server.view.index.ViewIndexDefinition.TYPE_VIEW;
  43. public class ViewIndexer implements ResilientIndexer {
  44. private final DbClient dbClient;
  45. private final EsClient esClient;
  46. public ViewIndexer(DbClient dbClient, EsClient esClient) {
  47. this.dbClient = dbClient;
  48. this.esClient = esClient;
  49. }
  50. @Override
  51. public Set<IndexType> getIndexTypes() {
  52. return Set.of(TYPE_VIEW);
  53. }
  54. @Override
  55. public void indexOnStartup(Set<IndexType> uninitializedIndexTypes) {
  56. indexAll(Size.LARGE);
  57. }
  58. public void indexAll() {
  59. indexAll(Size.REGULAR);
  60. }
  61. private void indexAll(Size bulkSize) {
  62. try (DbSession dbSession = dbClient.openSession(false)) {
  63. Map<String, String> rootViewUuidByViewUuid = new HashMap<>();
  64. for (UuidWithBranchUuidDto uuidWithBranchUuidDto : dbClient.componentDao().selectAllViewsAndSubViews(dbSession)) {
  65. rootViewUuidByViewUuid.put(uuidWithBranchUuidDto.getUuid(), uuidWithBranchUuidDto.getBranchUuid());
  66. }
  67. index(dbSession, rootViewUuidByViewUuid, false, bulkSize);
  68. }
  69. }
  70. /**
  71. * Index a root view : it will fetch a view and its subviews from the DB and index them.
  72. * Used by the compute engine to reindex a root view.
  73. * <p/>
  74. * The views lookup cache will be cleared
  75. */
  76. public void index(String rootViewUuid) {
  77. try (DbSession dbSession = dbClient.openSession(false)) {
  78. Map<String, String> viewAndRootViewUuidMap = new HashMap<>();
  79. for (ComponentDto viewOrSubView : dbClient.componentDao().selectEnabledViewsFromRootView(dbSession, rootViewUuid)) {
  80. viewAndRootViewUuidMap.put(viewOrSubView.uuid(), viewOrSubView.branchUuid());
  81. }
  82. index(dbSession, viewAndRootViewUuidMap, true, Size.REGULAR);
  83. }
  84. }
  85. /**
  86. * Index a single document.
  87. * <p/>
  88. * The views lookup cache will be cleared
  89. */
  90. public void index(ViewDoc viewDoc) {
  91. BulkIndexer bulk = new BulkIndexer(esClient, TYPE_VIEW, Size.REGULAR);
  92. bulk.start();
  93. doIndex(bulk, viewDoc, true);
  94. bulk.stop();
  95. }
  96. private void index(DbSession dbSession, Map<String, String> rootViewUuidByViewUuid, boolean needClearCache, Size bulkSize) {
  97. BulkIndexer bulk = new BulkIndexer(esClient, TYPE_VIEW, bulkSize);
  98. bulk.start();
  99. for (Map.Entry<String, String> entry : rootViewUuidByViewUuid.entrySet()) {
  100. String viewUuid = entry.getKey();
  101. String rootViewUuid = entry.getValue();
  102. List<String> projectBranchUuids = dbClient.componentDao().selectProjectBranchUuidsFromView(dbSession, viewUuid, rootViewUuid);
  103. doIndex(bulk, new ViewDoc()
  104. .setUuid(viewUuid)
  105. .setProjectBranchUuids(projectBranchUuids), needClearCache);
  106. }
  107. bulk.stop();
  108. }
  109. private void doIndex(BulkIndexer bulk, ViewDoc viewDoc, boolean needClearCache) {
  110. bulk.add(newIndexRequest(viewDoc));
  111. if (needClearCache) {
  112. clearLookupCache(viewDoc.uuid());
  113. }
  114. }
  115. private static IndexRequest newIndexRequest(ViewDoc doc) {
  116. return new IndexRequest(TYPE_VIEW.getIndex().getName())
  117. .id(doc.getId())
  118. .routing(doc.getRouting().orElse(null))
  119. .source(doc.getFields());
  120. }
  121. private void clearLookupCache(String viewUuid) {
  122. try {
  123. esClient.clearCache(new ClearIndicesCacheRequest().queryCache(true));
  124. } catch (Exception e) {
  125. throw new IllegalStateException(String.format("Unable to clear lookup cache of view '%s'", viewUuid), e);
  126. }
  127. }
  128. /**
  129. * This is based on the fact that a WebService is only calling {@link ViewIndexer#delete(DbSession, Collection)}
  130. * So the resiliency is only taking in account a deletion of view component
  131. * A safety check is done by not deleting any component that still exist in database.
  132. * This should not occur but prevent any misuse on this resiliency
  133. */
  134. @Override
  135. public IndexingResult index(DbSession dbSession, Collection<EsQueueDto> items) {
  136. if (items.isEmpty()) {
  137. return new IndexingResult();
  138. }
  139. Set<String> viewUuids = items
  140. .stream()
  141. .map(EsQueueDto::getDocId)
  142. .collect(Collectors.toSet());
  143. BulkIndexer bulkIndexer = newBulkIndexer(Size.REGULAR, new OneToOneResilientIndexingListener(dbClient, dbSession, items));
  144. bulkIndexer.start();
  145. // Safety check to remove all views that may not have been deleted
  146. viewUuids.removeAll(dbClient.componentDao().selectExistingUuids(dbSession, viewUuids));
  147. viewUuids.forEach(v -> bulkIndexer.addDeletion(TYPE_VIEW, v));
  148. return bulkIndexer.stop();
  149. }
  150. public void delete(DbSession dbSession, Collection<String> viewUuids) {
  151. List<EsQueueDto> items = viewUuids.stream()
  152. .map(l -> EsQueueDto.create(TYPE_VIEW.format(), l))
  153. .toList();
  154. dbClient.esQueueDao().insert(dbSession, items);
  155. dbSession.commit();
  156. index(dbSession, items);
  157. }
  158. private BulkIndexer newBulkIndexer(Size bulkSize, IndexingListener listener) {
  159. return new BulkIndexer(esClient, TYPE_VIEW, bulkSize, listener);
  160. }
  161. }