123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238 |
- /*
- * SonarQube
- * Copyright (C) 2009-2023 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
- package org.sonar.server.qualityprofile.index;
-
- import com.google.common.collect.ImmutableSet;
- import java.util.Collection;
- import java.util.HashMap;
- import java.util.List;
- import java.util.Map;
- import java.util.Set;
- import java.util.stream.Collectors;
- import javax.annotation.Nullable;
- import org.elasticsearch.action.index.IndexRequest;
- import org.elasticsearch.action.search.SearchRequest;
- import org.elasticsearch.index.query.QueryBuilders;
- import org.elasticsearch.search.builder.SearchSourceBuilder;
- import org.slf4j.Logger;
- import org.slf4j.LoggerFactory;
- import org.sonar.db.DbClient;
- import org.sonar.db.DbSession;
- import org.sonar.db.es.EsQueueDto;
- import org.sonar.db.qualityprofile.IndexedActiveRuleDto;
- import org.sonar.db.qualityprofile.QProfileDto;
- import org.sonar.db.qualityprofile.RulesProfileDto;
- import org.sonar.db.rule.SeverityUtil;
- import org.sonar.server.es.BulkIndexer;
- import org.sonar.server.es.BulkIndexer.Size;
- import org.sonar.server.es.EsClient;
- import org.sonar.server.es.IndexType;
- import org.sonar.server.es.IndexingListener;
- import org.sonar.server.es.IndexingResult;
- import org.sonar.server.es.OneToOneResilientIndexingListener;
- import org.sonar.server.es.ResilientIndexer;
- import org.sonar.server.qualityprofile.ActiveRuleChange;
- import org.sonar.server.qualityprofile.ActiveRuleInheritance;
-
- import static org.elasticsearch.index.query.QueryBuilders.termQuery;
- import static org.sonar.server.qualityprofile.index.ActiveRuleDoc.docIdOf;
- import static org.sonar.server.rule.index.RuleIndexDefinition.FIELD_ACTIVE_RULE_PROFILE_UUID;
- import static org.sonar.server.rule.index.RuleIndexDefinition.TYPE_ACTIVE_RULE;
-
- public class ActiveRuleIndexer implements ResilientIndexer {
-
- private static final Logger LOGGER = LoggerFactory.getLogger(ActiveRuleIndexer.class);
- private static final String ID_TYPE_ACTIVE_RULE_UUID = "activeRuleUuid";
- private static final String ID_TYPE_RULE_PROFILE_UUID = "ruleProfileUuid";
-
- private final DbClient dbClient;
- private final EsClient esClient;
-
- public ActiveRuleIndexer(DbClient dbClient, EsClient esClient) {
- this.dbClient = dbClient;
- this.esClient = esClient;
- }
-
- @Override
- public void indexOnStartup(Set<IndexType> uninitializedIndexTypes) {
- indexAll(Size.LARGE);
- }
-
- public void indexAll() {
- indexAll(Size.REGULAR);
- }
-
- private void indexAll(Size bulkSize) {
- try (DbSession dbSession = dbClient.openSession(false)) {
- BulkIndexer bulkIndexer = createBulkIndexer(bulkSize, IndexingListener.FAIL_ON_ERROR);
- bulkIndexer.start();
- dbClient.activeRuleDao().scrollAllForIndexing(dbSession, ar -> bulkIndexer.add(newIndexRequest(ar)));
- bulkIndexer.stop();
- }
- }
-
- @Override
- public Set<IndexType> getIndexTypes() {
- return ImmutableSet.of(TYPE_ACTIVE_RULE);
- }
-
- public void commitAndIndex(DbSession dbSession, Collection<ActiveRuleChange> changes) {
- List<EsQueueDto> items = changes.stream()
- .map(ActiveRuleChange::getActiveRule)
- .map(ar -> newQueueDto(docIdOf(ar.getUuid()), ID_TYPE_ACTIVE_RULE_UUID, ar.getRuleUuid()))
- .toList();
-
- dbClient.esQueueDao().insert(dbSession, items);
- dbSession.commit();
- postCommit(dbSession, items);
- }
-
- public void commitDeletionOfProfiles(DbSession dbSession, Collection<QProfileDto> profiles) {
- List<EsQueueDto> items = profiles.stream()
- .map(QProfileDto::getRulesProfileUuid)
- .distinct()
- .map(ruleProfileUuid -> newQueueDto(ruleProfileUuid, ID_TYPE_RULE_PROFILE_UUID, null))
- .toList();
-
- dbClient.esQueueDao().insert(dbSession, items);
- dbSession.commit();
- postCommit(dbSession, items);
- }
-
- /**
- * Entry point for Byteman tests. See directory tests/resilience.
- */
- private void postCommit(DbSession dbSession, Collection<EsQueueDto> items) {
- index(dbSession, items);
- }
-
- /**
- * @return the number of items that have been successfully indexed
- */
- @Override
- public IndexingResult index(DbSession dbSession, Collection<EsQueueDto> items) {
- IndexingResult result = new IndexingResult();
-
- if (items.isEmpty()) {
- return result;
- }
-
- Map<String, EsQueueDto> activeRuleItems = new HashMap<>();
- Map<String, EsQueueDto> ruleProfileItems = new HashMap<>();
- items.forEach(i -> {
- if (ID_TYPE_RULE_PROFILE_UUID.equals(i.getDocIdType())) {
- ruleProfileItems.put(i.getDocId(), i);
- } else if (ID_TYPE_ACTIVE_RULE_UUID.equals(i.getDocIdType())) {
- activeRuleItems.put(i.getDocId(), i);
- } else {
- LOGGER.error("Unsupported es_queue.doc_id_type. Removing row from queue: " + i);
- deleteQueueDto(dbSession, i);
- }
- });
-
- if (!activeRuleItems.isEmpty()) {
- result.add(doIndexActiveRules(dbSession, activeRuleItems));
- }
- if (!ruleProfileItems.isEmpty()) {
- result.add(doIndexRuleProfiles(dbSession, ruleProfileItems));
- }
- return result;
- }
-
- private IndexingResult doIndexActiveRules(DbSession dbSession, Map<String, EsQueueDto> activeRuleItems) {
- OneToOneResilientIndexingListener listener = new OneToOneResilientIndexingListener(dbClient, dbSession, activeRuleItems.values());
- BulkIndexer bulkIndexer = createBulkIndexer(Size.REGULAR, listener);
- bulkIndexer.start();
- Map<String, EsQueueDto> remaining = new HashMap<>(activeRuleItems);
- dbClient.activeRuleDao().scrollByUuidsForIndexing(dbSession, toActiveRuleUuids(activeRuleItems),
- i -> {
- remaining.remove(docIdOf(i.getUuid()));
- bulkIndexer.add(newIndexRequest(i));
- });
-
- // the remaining ids reference rows that don't exist in db. They must
- // be deleted from index.
- remaining.values().forEach(item -> bulkIndexer.addDeletion(TYPE_ACTIVE_RULE,
- item.getDocId(), item.getDocRouting()));
- return bulkIndexer.stop();
- }
-
- private static Collection<String> toActiveRuleUuids(Map<String, EsQueueDto> activeRuleItems) {
- Set<String> docIds = activeRuleItems.keySet();
- return docIds.stream()
- .map(ActiveRuleDoc::activeRuleUuidOf)
- .collect(Collectors.toSet());
- }
-
- private IndexingResult doIndexRuleProfiles(DbSession dbSession, Map<String, EsQueueDto> ruleProfileItems) {
- IndexingResult result = new IndexingResult();
-
- for (Map.Entry<String, EsQueueDto> entry : ruleProfileItems.entrySet()) {
- String ruleProfileUUid = entry.getKey();
- EsQueueDto item = entry.getValue();
- IndexingResult profileResult;
-
- RulesProfileDto profile = dbClient.qualityProfileDao().selectRuleProfile(dbSession, ruleProfileUUid);
- if (profile == null) {
- // profile does not exist anymore in db --> related documents must be deleted from index rules/activeRule
- SearchRequest search = EsClient.prepareSearch(TYPE_ACTIVE_RULE.getMainType())
- .source(new SearchSourceBuilder().query(QueryBuilders.boolQuery().must(termQuery(FIELD_ACTIVE_RULE_PROFILE_UUID, ruleProfileUUid))));
- profileResult = BulkIndexer.delete(esClient, TYPE_ACTIVE_RULE, search);
-
- } else {
- BulkIndexer bulkIndexer = createBulkIndexer(Size.REGULAR, IndexingListener.FAIL_ON_ERROR);
- bulkIndexer.start();
- dbClient.activeRuleDao().scrollByRuleProfileForIndexing(dbSession, ruleProfileUUid, i -> bulkIndexer.add(newIndexRequest(i)));
- profileResult = bulkIndexer.stop();
- }
-
- if (profileResult.isSuccess()) {
- deleteQueueDto(dbSession, item);
- }
- result.add(profileResult);
- }
-
- return result;
- }
-
- private void deleteQueueDto(DbSession dbSession, EsQueueDto item) {
- dbClient.esQueueDao().delete(dbSession, item);
- dbSession.commit();
- }
-
- private BulkIndexer createBulkIndexer(Size size, IndexingListener listener) {
- return new BulkIndexer(esClient, TYPE_ACTIVE_RULE, size, listener);
- }
-
- private static IndexRequest newIndexRequest(IndexedActiveRuleDto dto) {
- ActiveRuleDoc doc = new ActiveRuleDoc(dto.getUuid())
- .setRuleUuid(dto.getRuleUuid())
- .setRuleProfileUuid(dto.getRuleProfileUuid())
- .setSeverity(SeverityUtil.getSeverityFromOrdinal(dto.getSeverity()));
- // all the fields must be present, even if value is null
- String inheritance = dto.getInheritance();
- doc.setInheritance(inheritance == null ? ActiveRuleInheritance.NONE.name() : inheritance);
- return doc.toIndexRequest();
- }
-
- private static EsQueueDto newQueueDto(String docId, String docIdType, @Nullable String routing) {
- return EsQueueDto.create(TYPE_ACTIVE_RULE.format(), docId, docIdType, routing);
- }
- }
|