package org.sonar.server.es.metadata;
import java.util.Optional;
-import org.elasticsearch.action.get.GetRequestBuilder;
-import org.elasticsearch.action.get.GetResponse;
-import org.elasticsearch.common.document.DocumentField;
-import org.sonar.server.es.EsClient;
import org.sonar.server.es.Index;
import org.sonar.server.es.IndexType;
-import org.sonar.server.es.IndexType.IndexMainType;
-import org.sonar.server.es.IndexType.IndexRelationType;
-import static org.sonar.server.es.metadata.MetadataIndexDefinition.TYPE_METADATA;
-import static org.sonar.server.es.newindex.DefaultIndexSettings.REFRESH_IMMEDIATE;
+public interface MetadataIndex {
+ Optional<String> getHash(Index index);
-public class MetadataIndex {
+ void setHash(Index index, String hash);
- private static final String DB_VENDOR_KEY = "dbVendor";
+ boolean getInitialized(IndexType indexType);
- private final EsClient esClient;
+ void setInitialized(IndexType indexType, boolean initialized);
- public MetadataIndex(EsClient esClient) {
- this.esClient = esClient;
- }
+ Optional<String> getDbVendor();
- public Optional<String> getHash(Index index) {
- return getMetadata(hashId(index));
- }
-
- public void setHash(Index index, String hash) {
- setMetadata(hashId(index), hash);
- }
-
- private static String hashId(Index index) {
- return index.getName() + ".indexStructure";
- }
-
- public boolean getInitialized(IndexType indexType) {
- return getMetadata(initializedId(indexType)).map(Boolean::parseBoolean).orElse(false);
- }
-
- public void setInitialized(IndexType indexType, boolean initialized) {
- setMetadata(initializedId(indexType), String.valueOf(initialized));
- }
-
- private static String initializedId(IndexType indexType) {
- if (indexType instanceof IndexMainType) {
- IndexMainType mainType = (IndexMainType) indexType;
- return mainType.getIndex().getName() + "." + mainType.getType() + ".initialized";
- }
- if (indexType instanceof IndexRelationType) {
- IndexRelationType relationType = (IndexRelationType) indexType;
- IndexMainType mainType = relationType.getMainType();
- return mainType.getIndex().getName() + "." + mainType.getType() + "." + relationType.getName() + ".initialized";
- }
- throw new IllegalArgumentException("Unsupported IndexType " + indexType.getClass());
- }
-
- public Optional<String> getDbVendor() {
- return getMetadata(DB_VENDOR_KEY);
- }
-
- public void setDbMetadata(String vendor) {
- setMetadata(DB_VENDOR_KEY, vendor);
- }
-
- private Optional<String> getMetadata(String id) {
- GetRequestBuilder request = esClient.prepareGet(TYPE_METADATA, id)
- .setStoredFields(MetadataIndexDefinition.FIELD_VALUE);
- GetResponse response = request.get();
- if (response.isExists()) {
- DocumentField field = response.getField(MetadataIndexDefinition.FIELD_VALUE);
- return Optional.of(field.getValue());
- }
- return Optional.empty();
- }
-
- private void setMetadata(String id, String value) {
- esClient.prepareIndex(TYPE_METADATA)
- .setId(id)
- .setSource(MetadataIndexDefinition.FIELD_VALUE, value)
- .setRefreshPolicy(REFRESH_IMMEDIATE)
- .get();
- }
+ void setDbMetadata(String vendor);
}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.es.metadata;
+
+import java.util.Optional;
+import org.elasticsearch.action.get.GetRequestBuilder;
+import org.elasticsearch.action.get.GetResponse;
+import org.elasticsearch.common.document.DocumentField;
+import org.sonar.server.es.EsClient;
+import org.sonar.server.es.Index;
+import org.sonar.server.es.IndexType;
+import org.sonar.server.es.IndexType.IndexMainType;
+import org.sonar.server.es.IndexType.IndexRelationType;
+
+import static org.sonar.server.es.metadata.MetadataIndexDefinition.TYPE_METADATA;
+import static org.sonar.server.es.newindex.DefaultIndexSettings.REFRESH_IMMEDIATE;
+
+public class MetadataIndexImpl implements MetadataIndex {
+
+ private static final String DB_VENDOR_KEY = "dbVendor";
+
+ private final EsClient esClient;
+
+ public MetadataIndexImpl(EsClient esClient) {
+ this.esClient = esClient;
+ }
+
+ @Override
+ public Optional<String> getHash(Index index) {
+ return getMetadata(hashId(index));
+ }
+
+ @Override
+ public void setHash(Index index, String hash) {
+ setMetadata(hashId(index), hash);
+ }
+
+ private static String hashId(Index index) {
+ return index.getName() + ".indexStructure";
+ }
+
+ @Override
+ public boolean getInitialized(IndexType indexType) {
+ return getMetadata(initializedId(indexType)).map(Boolean::parseBoolean).orElse(false);
+ }
+
+ @Override
+ public void setInitialized(IndexType indexType, boolean initialized) {
+ setMetadata(initializedId(indexType), String.valueOf(initialized));
+ }
+
+ private static String initializedId(IndexType indexType) {
+ if (indexType instanceof IndexMainType) {
+ IndexMainType mainType = (IndexMainType) indexType;
+ return mainType.getIndex().getName() + "." + mainType.getType() + ".initialized";
+ }
+ if (indexType instanceof IndexRelationType) {
+ IndexRelationType relationType = (IndexRelationType) indexType;
+ IndexMainType mainType = relationType.getMainType();
+ return mainType.getIndex().getName() + "." + mainType.getType() + "." + relationType.getName() + ".initialized";
+ }
+ throw new IllegalArgumentException("Unsupported IndexType " + indexType.getClass());
+ }
+
+ @Override
+ public Optional<String> getDbVendor() {
+ return getMetadata(DB_VENDOR_KEY);
+ }
+
+ @Override
+ public void setDbMetadata(String vendor) {
+ setMetadata(DB_VENDOR_KEY, vendor);
+ }
+
+ private Optional<String> getMetadata(String id) {
+ GetRequestBuilder request = esClient.prepareGet(TYPE_METADATA, id)
+ .setStoredFields(MetadataIndexDefinition.FIELD_VALUE);
+ GetResponse response = request.get();
+ if (response.isExists()) {
+ DocumentField field = response.getField(MetadataIndexDefinition.FIELD_VALUE);
+ return Optional.of(field.getValue());
+ }
+ return Optional.empty();
+ }
+
+ private void setMetadata(String id, String value) {
+ esClient.prepareIndex(TYPE_METADATA)
+ .setId(id)
+ .setSource(MetadataIndexDefinition.FIELD_VALUE, value)
+ .setRefreshPolicy(REFRESH_IMMEDIATE)
+ .get();
+ }
+}
@Rule
public EsTester es = EsTester.createCustom(new MetadataIndexDefinitionBridge(), new FakeIndexDefinition());
- private final MetadataIndex underTest = new MetadataIndex(es.client());
+ private final MetadataIndex underTest = new MetadataIndexImpl(es.client());
private final String indexName = randomAlphabetic(20).toLowerCase(Locale.ENGLISH);
private final Index index = new Random().nextBoolean() ? Index.simple(indexName) : Index.withRelations(indexName);
@Override
public void markAsCompatible() {
- metadataIndex.setDbMetadata(getDbVendor());
+ if (!hasSameDbVendor()) {
+ metadataIndex.setDbMetadata(getDbVendor());
+ }
}
private String getDbVendor() {
import org.sonar.server.es.ProjectIndexersImpl;
import org.sonar.server.es.RecoveryIndexer;
import org.sonar.server.es.metadata.EsDbCompatibilityImpl;
-import org.sonar.server.es.metadata.MetadataIndex;
import org.sonar.server.es.metadata.MetadataIndexDefinition;
+import org.sonar.server.es.metadata.MetadataIndexImpl;
import org.sonar.server.extension.CoreExtensionBootstraper;
import org.sonar.server.extension.CoreExtensionStopper;
import org.sonar.server.favorite.FavoriteModule;
addIfStartupLeader(
IndexCreator.class,
MetadataIndexDefinition.class,
- MetadataIndex.class,
+ MetadataIndexImpl.class,
EsDbCompatibilityImpl.class);
addIfCluster(NodeHealthModule.class);
import org.sonar.server.es.IndexType.IndexMainType;
import org.sonar.server.es.metadata.MetadataIndex;
import org.sonar.server.es.metadata.MetadataIndexDefinition;
+import org.sonar.server.es.metadata.MetadataIndexImpl;
import org.sonar.server.es.newindex.NewRegularIndex;
import org.sonar.server.es.newindex.SettingsConfiguration;
import org.sonar.server.platform.db.migration.es.MigrationEsClient;
public EsTester es = EsTester.createCustom();
private MetadataIndexDefinition metadataIndexDefinition = new MetadataIndexDefinition(new MapSettings().asConfig());
- private MetadataIndex metadataIndex = new MetadataIndex(es.client());
+ private MetadataIndex metadataIndex = new MetadataIndexImpl(es.client());
private TestEsDbCompatibility esDbCompatibility = new TestEsDbCompatibility();
private MapSettings settings = new MapSettings();
private MigrationEsClient migrationEsClient = mock(MigrationEsClient.class);
import org.mockito.Mockito;
import org.sonar.api.config.internal.MapSettings;
import org.sonar.server.es.metadata.MetadataIndex;
+import org.sonar.server.es.metadata.MetadataIndexImpl;
import org.sonar.server.es.newindex.FakeIndexDefinition;
import static org.mockito.ArgumentMatchers.eq;
public EsTester es = EsTester.createCustom(new FakeIndexDefinition());
private final MapSettings settings = new MapSettings();
- private final MetadataIndex metadataIndex = mock(MetadataIndex.class);
+ private final MetadataIndex metadataIndex = mock(MetadataIndexImpl.class);
private final StartupIndexer indexer = mock(StartupIndexer.class);
private final IndexerStartupTask underTest = new IndexerStartupTask(es.client(), settings.asConfig(), metadataIndex, indexer);
*/
package org.sonar.server.es.metadata;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+import javax.annotation.CheckForNull;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.mockito.Mockito;
import org.sonar.db.DbClient;
-import org.sonar.server.es.EsTester;
-import org.sonar.server.es.newindex.FakeIndexDefinition;
+import org.sonar.server.es.Index;
+import org.sonar.server.es.IndexType;
+
+import static org.apache.commons.lang.RandomStringUtils.randomAlphabetic;
import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
public class EsDbCompatibilityImplTest {
@Rule
public ExpectedException expectedException = ExpectedException.none();
- @Rule
- public EsTester es = EsTester.createCustom(new MetadataIndexDefinitionBridge(), new FakeIndexDefinition());
+
private DbClient dbClient = mock(DbClient.class, Mockito.RETURNS_DEEP_STUBS);
- private MetadataIndex metadataIndex = new MetadataIndex(es.client());
+ private MetadataIndex metadataIndex = spy(new TestMetadataIndex());
private EsDbCompatibilityImpl underTest = new EsDbCompatibilityImpl(dbClient, metadataIndex);
@Test
}
@Test
- public void store_db_metadata_in_es() {
+ public void markAsCompatible_db_metadata_in_es() {
prepareDb("mysql");
underTest.markAsCompatible();
}
@Test
- public void store_updates_db_metadata_in_es() {
+ public void markAsCompatible_updates_db_metadata_in_es() {
prepareEs("mysql");
prepareDb("postgres");
}
@Test
- public void store_marks_es_as_compatible_with_db() {
+ public void markAsCompatible_marks_es_as_compatible_with_db() {
prepareDb("postgres");
underTest.markAsCompatible();
assertThat(underTest.hasSameDbVendor()).isTrue();
}
+ @Test
+ public void markAsCompatible_has_no_effect_if_vendor_is_the_same() {
+ String vendor = randomAlphabetic(12);
+ prepareEs(vendor);
+ prepareDb(vendor);
+
+ underTest.markAsCompatible();
+
+ assertThat(underTest.hasSameDbVendor()).isTrue();
+ verify(metadataIndex, times(0)).setDbMetadata(anyString());
+ }
+
private void prepareDb(String dbVendor) {
when(dbClient.getDatabase().getDialect().getId()).thenReturn(dbVendor);
}
private void prepareEs(String dbVendor) {
metadataIndex.setDbMetadata(dbVendor);
+ // reset spy to not perturbate assertions on spy from verified code
+ reset(metadataIndex);
+ }
+
+ private static class TestMetadataIndex implements MetadataIndex {
+ private final Map<Index, String> hashes = new HashMap<>();
+ private final Map<IndexType, Boolean> initializeds = new HashMap<>();
+ @CheckForNull
+ private String dbVendor = null;
+
+ @Override
+ public Optional<String> getHash(Index index) {
+ return Optional.ofNullable(hashes.get(index));
+ }
+
+ @Override
+ public void setHash(Index index, String hash) {
+ hashes.put(index, hash);
+ }
+
+ @Override
+ public boolean getInitialized(IndexType indexType) {
+ return initializeds.getOrDefault(indexType, false);
+ }
+
+ @Override
+ public void setInitialized(IndexType indexType, boolean initialized) {
+ initializeds.put(indexType, initialized);
+ }
+
+ @Override
+ public Optional<String> getDbVendor() {
+ return Optional.ofNullable(dbVendor);
+ }
+
+ @Override
+ public void setDbMetadata(String vendor) {
+ this.dbVendor = vendor;
+ }
}
}