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.

ActiveRuleIndexer.java 9.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238
  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.qualityprofile.index;
  21. import com.google.common.collect.ImmutableSet;
  22. import java.util.Collection;
  23. import java.util.HashMap;
  24. import java.util.List;
  25. import java.util.Map;
  26. import java.util.Set;
  27. import java.util.stream.Collectors;
  28. import javax.annotation.Nullable;
  29. import org.elasticsearch.action.index.IndexRequest;
  30. import org.elasticsearch.action.search.SearchRequest;
  31. import org.elasticsearch.index.query.QueryBuilders;
  32. import org.elasticsearch.search.builder.SearchSourceBuilder;
  33. import org.slf4j.Logger;
  34. import org.slf4j.LoggerFactory;
  35. import org.sonar.db.DbClient;
  36. import org.sonar.db.DbSession;
  37. import org.sonar.db.es.EsQueueDto;
  38. import org.sonar.db.qualityprofile.IndexedActiveRuleDto;
  39. import org.sonar.db.qualityprofile.QProfileDto;
  40. import org.sonar.db.qualityprofile.RulesProfileDto;
  41. import org.sonar.db.rule.SeverityUtil;
  42. import org.sonar.server.es.BulkIndexer;
  43. import org.sonar.server.es.BulkIndexer.Size;
  44. import org.sonar.server.es.EsClient;
  45. import org.sonar.server.es.IndexType;
  46. import org.sonar.server.es.IndexingListener;
  47. import org.sonar.server.es.IndexingResult;
  48. import org.sonar.server.es.OneToOneResilientIndexingListener;
  49. import org.sonar.server.es.ResilientIndexer;
  50. import org.sonar.server.qualityprofile.ActiveRuleChange;
  51. import org.sonar.server.qualityprofile.ActiveRuleInheritance;
  52. import static org.elasticsearch.index.query.QueryBuilders.termQuery;
  53. import static org.sonar.server.qualityprofile.index.ActiveRuleDoc.docIdOf;
  54. import static org.sonar.server.rule.index.RuleIndexDefinition.FIELD_ACTIVE_RULE_PROFILE_UUID;
  55. import static org.sonar.server.rule.index.RuleIndexDefinition.TYPE_ACTIVE_RULE;
  56. public class ActiveRuleIndexer implements ResilientIndexer {
  57. private static final Logger LOGGER = LoggerFactory.getLogger(ActiveRuleIndexer.class);
  58. private static final String ID_TYPE_ACTIVE_RULE_UUID = "activeRuleUuid";
  59. private static final String ID_TYPE_RULE_PROFILE_UUID = "ruleProfileUuid";
  60. private final DbClient dbClient;
  61. private final EsClient esClient;
  62. public ActiveRuleIndexer(DbClient dbClient, EsClient esClient) {
  63. this.dbClient = dbClient;
  64. this.esClient = esClient;
  65. }
  66. @Override
  67. public void indexOnStartup(Set<IndexType> uninitializedIndexTypes) {
  68. indexAll(Size.LARGE);
  69. }
  70. public void indexAll() {
  71. indexAll(Size.REGULAR);
  72. }
  73. private void indexAll(Size bulkSize) {
  74. try (DbSession dbSession = dbClient.openSession(false)) {
  75. BulkIndexer bulkIndexer = createBulkIndexer(bulkSize, IndexingListener.FAIL_ON_ERROR);
  76. bulkIndexer.start();
  77. dbClient.activeRuleDao().scrollAllForIndexing(dbSession, ar -> bulkIndexer.add(newIndexRequest(ar)));
  78. bulkIndexer.stop();
  79. }
  80. }
  81. @Override
  82. public Set<IndexType> getIndexTypes() {
  83. return ImmutableSet.of(TYPE_ACTIVE_RULE);
  84. }
  85. public void commitAndIndex(DbSession dbSession, Collection<ActiveRuleChange> changes) {
  86. List<EsQueueDto> items = changes.stream()
  87. .map(ActiveRuleChange::getActiveRule)
  88. .map(ar -> newQueueDto(docIdOf(ar.getUuid()), ID_TYPE_ACTIVE_RULE_UUID, ar.getRuleUuid()))
  89. .toList();
  90. dbClient.esQueueDao().insert(dbSession, items);
  91. dbSession.commit();
  92. postCommit(dbSession, items);
  93. }
  94. public void commitDeletionOfProfiles(DbSession dbSession, Collection<QProfileDto> profiles) {
  95. List<EsQueueDto> items = profiles.stream()
  96. .map(QProfileDto::getRulesProfileUuid)
  97. .distinct()
  98. .map(ruleProfileUuid -> newQueueDto(ruleProfileUuid, ID_TYPE_RULE_PROFILE_UUID, null))
  99. .toList();
  100. dbClient.esQueueDao().insert(dbSession, items);
  101. dbSession.commit();
  102. postCommit(dbSession, items);
  103. }
  104. /**
  105. * Entry point for Byteman tests. See directory tests/resilience.
  106. */
  107. private void postCommit(DbSession dbSession, Collection<EsQueueDto> items) {
  108. index(dbSession, items);
  109. }
  110. /**
  111. * @return the number of items that have been successfully indexed
  112. */
  113. @Override
  114. public IndexingResult index(DbSession dbSession, Collection<EsQueueDto> items) {
  115. IndexingResult result = new IndexingResult();
  116. if (items.isEmpty()) {
  117. return result;
  118. }
  119. Map<String, EsQueueDto> activeRuleItems = new HashMap<>();
  120. Map<String, EsQueueDto> ruleProfileItems = new HashMap<>();
  121. items.forEach(i -> {
  122. if (ID_TYPE_RULE_PROFILE_UUID.equals(i.getDocIdType())) {
  123. ruleProfileItems.put(i.getDocId(), i);
  124. } else if (ID_TYPE_ACTIVE_RULE_UUID.equals(i.getDocIdType())) {
  125. activeRuleItems.put(i.getDocId(), i);
  126. } else {
  127. LOGGER.error("Unsupported es_queue.doc_id_type. Removing row from queue: " + i);
  128. deleteQueueDto(dbSession, i);
  129. }
  130. });
  131. if (!activeRuleItems.isEmpty()) {
  132. result.add(doIndexActiveRules(dbSession, activeRuleItems));
  133. }
  134. if (!ruleProfileItems.isEmpty()) {
  135. result.add(doIndexRuleProfiles(dbSession, ruleProfileItems));
  136. }
  137. return result;
  138. }
  139. private IndexingResult doIndexActiveRules(DbSession dbSession, Map<String, EsQueueDto> activeRuleItems) {
  140. OneToOneResilientIndexingListener listener = new OneToOneResilientIndexingListener(dbClient, dbSession, activeRuleItems.values());
  141. BulkIndexer bulkIndexer = createBulkIndexer(Size.REGULAR, listener);
  142. bulkIndexer.start();
  143. Map<String, EsQueueDto> remaining = new HashMap<>(activeRuleItems);
  144. dbClient.activeRuleDao().scrollByUuidsForIndexing(dbSession, toActiveRuleUuids(activeRuleItems),
  145. i -> {
  146. remaining.remove(docIdOf(i.getUuid()));
  147. bulkIndexer.add(newIndexRequest(i));
  148. });
  149. // the remaining ids reference rows that don't exist in db. They must
  150. // be deleted from index.
  151. remaining.values().forEach(item -> bulkIndexer.addDeletion(TYPE_ACTIVE_RULE,
  152. item.getDocId(), item.getDocRouting()));
  153. return bulkIndexer.stop();
  154. }
  155. private static Collection<String> toActiveRuleUuids(Map<String, EsQueueDto> activeRuleItems) {
  156. Set<String> docIds = activeRuleItems.keySet();
  157. return docIds.stream()
  158. .map(ActiveRuleDoc::activeRuleUuidOf)
  159. .collect(Collectors.toSet());
  160. }
  161. private IndexingResult doIndexRuleProfiles(DbSession dbSession, Map<String, EsQueueDto> ruleProfileItems) {
  162. IndexingResult result = new IndexingResult();
  163. for (Map.Entry<String, EsQueueDto> entry : ruleProfileItems.entrySet()) {
  164. String ruleProfileUUid = entry.getKey();
  165. EsQueueDto item = entry.getValue();
  166. IndexingResult profileResult;
  167. RulesProfileDto profile = dbClient.qualityProfileDao().selectRuleProfile(dbSession, ruleProfileUUid);
  168. if (profile == null) {
  169. // profile does not exist anymore in db --> related documents must be deleted from index rules/activeRule
  170. SearchRequest search = EsClient.prepareSearch(TYPE_ACTIVE_RULE.getMainType())
  171. .source(new SearchSourceBuilder().query(QueryBuilders.boolQuery().must(termQuery(FIELD_ACTIVE_RULE_PROFILE_UUID, ruleProfileUUid))));
  172. profileResult = BulkIndexer.delete(esClient, TYPE_ACTIVE_RULE, search);
  173. } else {
  174. BulkIndexer bulkIndexer = createBulkIndexer(Size.REGULAR, IndexingListener.FAIL_ON_ERROR);
  175. bulkIndexer.start();
  176. dbClient.activeRuleDao().scrollByRuleProfileForIndexing(dbSession, ruleProfileUUid, i -> bulkIndexer.add(newIndexRequest(i)));
  177. profileResult = bulkIndexer.stop();
  178. }
  179. if (profileResult.isSuccess()) {
  180. deleteQueueDto(dbSession, item);
  181. }
  182. result.add(profileResult);
  183. }
  184. return result;
  185. }
  186. private void deleteQueueDto(DbSession dbSession, EsQueueDto item) {
  187. dbClient.esQueueDao().delete(dbSession, item);
  188. dbSession.commit();
  189. }
  190. private BulkIndexer createBulkIndexer(Size size, IndexingListener listener) {
  191. return new BulkIndexer(esClient, TYPE_ACTIVE_RULE, size, listener);
  192. }
  193. private static IndexRequest newIndexRequest(IndexedActiveRuleDto dto) {
  194. ActiveRuleDoc doc = new ActiveRuleDoc(dto.getUuid())
  195. .setRuleUuid(dto.getRuleUuid())
  196. .setRuleProfileUuid(dto.getRuleProfileUuid())
  197. .setSeverity(SeverityUtil.getSeverityFromOrdinal(dto.getSeverity()));
  198. // all the fields must be present, even if value is null
  199. String inheritance = dto.getInheritance();
  200. doc.setInheritance(inheritance == null ? ActiveRuleInheritance.NONE.name() : inheritance);
  201. return doc.toIndexRequest();
  202. }
  203. private static EsQueueDto newQueueDto(String docId, String docIdType, @Nullable String routing) {
  204. return EsQueueDto.create(TYPE_ACTIVE_RULE.format(), docId, docIdType, routing);
  205. }
  206. }