Try to improve performance of MeasurePersister

This commit is contained in:
Julien HENRY 2014-06-18 10:10:36 +02:00
parent 383f667695
commit f3ce69889f
30 changed files with 170 additions and 114 deletions

View File

@ -813,7 +813,7 @@
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.2.2</version>
<version>3.2.7</version>
</dependency>
<dependency>
<groupId>org.picocontainer</groupId>

View File

@ -1,29 +0,0 @@
/*
* SonarQube, open source software quality management tool.
* Copyright (C) 2008-2014 SonarSource
* mailto:contact AT sonarsource DOT com
*
* SonarQube 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.
*
* SonarQube 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.batch.api;
/**
* Dependency Injection : all the classes implementing this interface are available in the batch IoC container.
* Just add a parameter to the constructor of your component.
*
* @since 4.4
*/
public interface BatchComponent {
}

View File

@ -1,30 +0,0 @@
/*
* SonarQube, open source software quality management tool.
* Copyright (C) 2008-2014 SonarSource
* mailto:contact AT sonarsource DOT com
*
* SonarQube 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.
*
* SonarQube 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.batch.api;
/**
* Batch extension point.
*
* @since 4.4
*/
public interface BatchExtension extends BatchComponent {
}

View File

@ -1,46 +0,0 @@
/*
* SonarQube, open source software quality management tool.
* Copyright (C) 2008-2014 SonarSource
* mailto:contact AT sonarsource DOT com
*
* SonarQube 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.
*
* SonarQube 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.batch.api;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Define instantiation strategy of batch extensions. If an extension is not annotated, then default value
* is {@link #PER_PROJECT}.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface InstantiationStrategy {
/**
* Shared extension. Lifecycle is the full analysis.
*/
String PER_BATCH = "PER_BATCH";
/**
* Created and initialized for each project and sub-project (a project is a module in Maven terminology).
*/
String PER_PROJECT = "PER_PROJECT";
String value();
}

View File

@ -25,6 +25,8 @@ import com.persistit.Exchange;
import com.persistit.Persistit;
import com.persistit.Value;
import com.persistit.Volume;
import com.persistit.encoding.CoderManager;
import com.persistit.encoding.ValueCoder;
import com.persistit.exception.PersistitException;
import com.persistit.logging.Slf4jAdapter;
import org.apache.commons.io.FileUtils;
@ -78,6 +80,11 @@ public class Caches implements BatchComponent, Startable {
}
}
public void registerValueCoder(Class<?> clazz, ValueCoder coder) {
CoderManager cm = persistit.getCoderManager();
cm.registerValueCoder(clazz, coder);
}
public <V extends Serializable> Cache<V> createCache(String cacheName) {
Preconditions.checkState(volume != null && volume.isOpened(), "Caches are not initialized");
Preconditions.checkState(!cacheNames.contains(cacheName), "Cache is already created: " + cacheName);

View File

@ -22,8 +22,10 @@ package org.sonar.batch.scan.measure;
import com.google.common.base.Preconditions;
import org.sonar.api.BatchComponent;
import org.sonar.api.measures.Measure;
import org.sonar.api.measures.MetricFinder;
import org.sonar.api.measures.RuleMeasure;
import org.sonar.api.resources.Resource;
import org.sonar.api.technicaldebt.batch.TechnicalDebtModel;
import org.sonar.batch.index.Cache;
import org.sonar.batch.index.Cache.Entry;
import org.sonar.batch.index.Caches;
@ -35,7 +37,8 @@ public class MeasureCache implements BatchComponent {
private final Cache<Measure> cache;
public MeasureCache(Caches caches) {
public MeasureCache(Caches caches, MetricFinder metricFinder, TechnicalDebtModel techDebtModel) {
caches.registerValueCoder(Measure.class, new MeasureValueCoder(metricFinder, techDebtModel));
cache = caches.createCache("measures");
}

View File

@ -0,0 +1,86 @@
/*
* SonarQube, open source software quality management tool.
* Copyright (C) 2008-2014 SonarSource
* mailto:contact AT sonarsource DOT com
*
* SonarQube 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.
*
* SonarQube 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.batch.scan.measure;
import com.persistit.Value;
import com.persistit.encoding.CoderContext;
import com.persistit.encoding.ValueCoder;
import org.sonar.api.measures.Measure;
import org.sonar.api.measures.Metric;
import org.sonar.api.measures.MetricFinder;
import org.sonar.api.measures.PersistenceMode;
import org.sonar.api.technicaldebt.batch.TechnicalDebtModel;
class MeasureValueCoder implements ValueCoder {
private final MetricFinder metricFinder;
private final TechnicalDebtModel techDebtModel;
public MeasureValueCoder(MetricFinder metricFinder, TechnicalDebtModel techDebtModel) {
this.metricFinder = metricFinder;
this.techDebtModel = techDebtModel;
}
public void put(Value value, Object object, CoderContext context) {
Measure<?> m = (Measure) object;
value.putString(m.getMetricKey());
value.put(m.getValue());
value.putString(m.getData());
value.putString(m.getDescription());
value.putString(m.getAlertStatus() != null ? m.getAlertStatus().name() : null);
value.putString(m.getAlertText());
value.put(m.getTendency());
value.putDate(m.getDate());
value.put(m.getVariation1());
value.put(m.getVariation2());
value.put(m.getVariation3());
value.put(m.getVariation4());
value.put(m.getVariation5());
value.putString(m.getUrl());
value.put(m.getCharacteristic() != null ? m.getCharacteristic().id() : null);
value.put(m.getRequirement() != null ? m.getRequirement().id() : null);
value.put(m.getPersonId() != null ? m.getPersonId().intValue() : null);
value.putString(m.getPersistenceMode() != null ? m.getPersistenceMode().name() : null);
}
public Object get(Value value, Class clazz, CoderContext context) {
Measure<?> m = new Measure();
String metricKey = value.getString();
m.setMetric(metricFinder.findByKey(metricKey));
m.setRawValue(value.isNull(true) ? null : value.getDouble());
m.setData(value.getString());
m.setDescription(value.getString());
m.setAlertStatus(value.isNull(true) ? null : Metric.Level.valueOf(value.getString()));
m.setAlertText(value.getString());
m.setTendency(value.isNull(true) ? null : value.getInt());
m.setDate(value.getDate());
m.setVariation1(value.isNull(true) ? null : value.getDouble());
m.setVariation2(value.isNull(true) ? null : value.getDouble());
m.setVariation3(value.isNull(true) ? null : value.getDouble());
m.setVariation4(value.isNull(true) ? null : value.getDouble());
m.setVariation5(value.isNull(true) ? null : value.getDouble());
m.setUrl(value.getString());
m.setCharacteristic(value.isNull(true) ? null : techDebtModel.characteristicById(value.getInt()));
m.setRequirement(value.isNull(true) ? null : techDebtModel.requirementsById(value.getInt()));
m.setPersonId(value.isNull(true) ? null : value.getInt());
m.setPersistenceMode(value.isNull(true) ? null : PersistenceMode.valueOf(value.getString()));
return m;
}
}

View File

@ -19,6 +19,7 @@
*/
package org.sonar.batch.scan.measure;
import org.apache.commons.lang.builder.EqualsBuilder;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
@ -27,6 +28,8 @@ import org.junit.rules.ExpectedException;
import org.junit.rules.TemporaryFolder;
import org.sonar.api.measures.CoreMetrics;
import org.sonar.api.measures.Measure;
import org.sonar.api.measures.Metric.Level;
import org.sonar.api.measures.MetricFinder;
import org.sonar.api.measures.RuleMeasure;
import org.sonar.api.resources.Directory;
import org.sonar.api.resources.File;
@ -34,6 +37,9 @@ import org.sonar.api.resources.Project;
import org.sonar.api.resources.Resource;
import org.sonar.api.rule.RuleKey;
import org.sonar.api.rules.RulePriority;
import org.sonar.api.technicaldebt.batch.Characteristic;
import org.sonar.api.technicaldebt.batch.Requirement;
import org.sonar.api.technicaldebt.batch.TechnicalDebtModel;
import org.sonar.api.technicaldebt.batch.internal.DefaultCharacteristic;
import org.sonar.batch.index.Cache.Entry;
import org.sonar.batch.index.Caches;
@ -43,6 +49,8 @@ import java.util.Date;
import java.util.Iterator;
import static org.fest.assertions.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
public class MeasureCacheTest {
@ -54,10 +62,17 @@ public class MeasureCacheTest {
Caches caches;
private MetricFinder metricFinder;
private TechnicalDebtModel techDebtModel;
@Before
public void start() throws Exception {
caches = CachesTest.createCacheOnTemp(temp);
caches.start();
metricFinder = mock(MetricFinder.class);
when(metricFinder.findByKey(CoreMetrics.NCLOC_KEY)).thenReturn(CoreMetrics.NCLOC);
techDebtModel = mock(TechnicalDebtModel.class);
}
@After
@ -67,7 +82,7 @@ public class MeasureCacheTest {
@Test
public void should_add_measure() throws Exception {
MeasureCache cache = new MeasureCache(caches);
MeasureCache cache = new MeasureCache(caches, metricFinder, techDebtModel);
Project p = new Project("struts");
assertThat(cache.entries()).hasSize(0);
@ -102,7 +117,7 @@ public class MeasureCacheTest {
*/
@Test
public void should_add_measure_with_big_data() throws Exception {
MeasureCache cache = new MeasureCache(caches);
MeasureCache cache = new MeasureCache(caches, metricFinder, techDebtModel);
Project p = new Project("struts");
assertThat(cache.entries()).hasSize(0);
@ -142,7 +157,7 @@ public class MeasureCacheTest {
*/
@Test
public void should_add_measure_with_too_big_data_for_persistit_pre_patch() throws Exception {
MeasureCache cache = new MeasureCache(caches);
MeasureCache cache = new MeasureCache(caches, metricFinder, techDebtModel);
Project p = new Project("struts");
assertThat(cache.entries()).hasSize(0);
@ -178,7 +193,7 @@ public class MeasureCacheTest {
@Test
public void should_add_measure_with_too_big_data_for_persistit() throws Exception {
MeasureCache cache = new MeasureCache(caches);
MeasureCache cache = new MeasureCache(caches, metricFinder, techDebtModel);
Project p = new Project("struts");
assertThat(cache.entries()).hasSize(0);
@ -201,7 +216,7 @@ public class MeasureCacheTest {
@Test
public void should_add_measure_with_same_metric() throws Exception {
MeasureCache cache = new MeasureCache(caches);
MeasureCache cache = new MeasureCache(caches, metricFinder, techDebtModel);
Project p = new Project("struts");
assertThat(cache.entries()).hasSize(0);
@ -223,7 +238,7 @@ public class MeasureCacheTest {
@Test
public void should_get_measures() throws Exception {
MeasureCache cache = new MeasureCache(caches);
MeasureCache cache = new MeasureCache(caches, metricFinder, techDebtModel);
Project p = new Project("struts");
Resource dir = new Directory("foo/bar").setEffectiveKey("struts:foo/bar");
Resource file1 = new File("foo/bar/File1.txt").setEffectiveKey("struts:foo/bar/File1.txt");
@ -260,4 +275,46 @@ public class MeasureCacheTest {
assertThat(cache.byResource(dir)).hasSize(1);
assertThat(cache.byResource(dir).iterator().next()).isEqualTo(mDir);
}
@Test
public void test_measure_coder() throws Exception {
MeasureCache cache = new MeasureCache(caches, metricFinder, techDebtModel);
Resource file1 = new File("foo/bar/File1.txt").setEffectiveKey("struts:foo/bar/File1.txt");
Measure measure = new Measure(CoreMetrics.NCLOC, 1.786, 5);
cache.put(file1, measure);
Measure savedMeasure = cache.byResource(file1).iterator().next();
assertThat(EqualsBuilder.reflectionEquals(measure, savedMeasure)).isTrue();
measure = new Measure(CoreMetrics.NCLOC);
measure.setData("data");
measure.setAlertStatus(Level.ERROR);
measure.setAlertText("alert");
Characteristic c = mock(Characteristic.class);
when(c.id()).thenReturn(1);
when(techDebtModel.characteristicById(1)).thenReturn(c);
measure.setCharacteristic(c);
measure.setDate(new Date());
measure.setDescription("description");
measure.setPersistenceMode(null);
measure.setPersonId(3);
Requirement r = mock(Requirement.class);
when(r.id()).thenReturn(7);
when(techDebtModel.requirementsById(7)).thenReturn(r);
measure.setRequirement(r);
measure.setTendency(4);
measure.setUrl("http://foo");
measure.setVariation1(11.0);
measure.setVariation2(12.0);
measure.setVariation3(13.0);
measure.setVariation4(14.0);
measure.setVariation5(15.0);
cache.put(file1, measure);
savedMeasure = cache.byResource(file1).iterator().next();
assertThat(EqualsBuilder.reflectionEquals(measure, savedMeasure)).isTrue();
}
}

View File

@ -4,7 +4,7 @@
<mapper namespace="org.sonar.core.source.db.SnapshotSourceMapper">
<select id="selectSnapshotSource" parameterType="int" resultType="string">
<select id="selectSnapshotSource" parameterType="long" resultType="string">
SELECT data
FROM snapshot_sources
WHERE snapshot_id = #{sid}

View File

@ -289,6 +289,14 @@ public class Measure<G extends Serializable> implements Serializable {
return setValue(v, DEFAULT_PRECISION);
}
/**
* For internal use
*/
public Measure setRawValue(@Nullable Double v) {
this.value = v;
return this;
}
/**
* Sets the measure value as an int
*