diff options
author | Simon Brandhof <simon.brandhof@gmail.com> | 2013-09-20 13:17:15 +0200 |
---|---|---|
committer | Simon Brandhof <simon.brandhof@gmail.com> | 2013-09-20 13:17:25 +0200 |
commit | d46362bfbaa7a743d84290ef632ad8f21e0777c5 (patch) | |
tree | 4baf69d5d2899b057dcd1bf2448fd874035ce3bd /sonar-server | |
parent | 7fe48fc80737fb3a2511c7a517d07ca00da7ea48 (diff) | |
download | sonarqube-d46362bfbaa7a743d84290ef632ad8f21e0777c5.tar.gz sonarqube-d46362bfbaa7a743d84290ef632ad8f21e0777c5.zip |
SONAR-4690 log progress status
Diffstat (limited to 'sonar-server')
8 files changed, 186 insertions, 25 deletions
diff --git a/sonar-server/src/main/java/org/sonar/server/db/migrations/violation/Progress.java b/sonar-server/src/main/java/org/sonar/server/db/migrations/violation/Progress.java new file mode 100644 index 00000000000..fe1175a8d7b --- /dev/null +++ b/sonar-server/src/main/java/org/sonar/server/db/migrations/violation/Progress.java @@ -0,0 +1,65 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2013 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.server.db.migrations.violation; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.TimerTask; +import java.util.concurrent.atomic.AtomicInteger; + +class Progress extends TimerTask { + + static final String THREAD_NAME = "Violation Migration Progress"; + static final long DELAY_MS = 60000L; + + private final AtomicInteger counter = new AtomicInteger(0); + private final Logger logger; + private final int totalViolations; + private long start = System.currentTimeMillis(); + + Progress(int totalViolations, Logger logger) { + this.totalViolations = totalViolations; + this.logger = logger; + } + + Progress(int totalViolations) { + this(totalViolations, LoggerFactory.getLogger(Progress.class)); + } + + void increment(int delta) { + counter.addAndGet(delta); + } + + @Override + public void run() { + int totalIssues = counter.get(); + long durationMinutes = (System.currentTimeMillis() - start) / 60000L; + int frequency = 0, remaining = 0; + if (durationMinutes > 0) { + frequency = (int) (totalIssues / durationMinutes); + remaining = (totalViolations - totalIssues) / frequency; + } + logger.info(String.format( + "%d%% [%d/%d violations, %d violations/minute, %d minutes remaining]", + (100 * totalIssues) / totalViolations, totalIssues, totalViolations, frequency, remaining) + ); + } +} diff --git a/sonar-server/src/main/java/org/sonar/server/db/migrations/violation/Referentials.java b/sonar-server/src/main/java/org/sonar/server/db/migrations/violation/Referentials.java index 43e14fa3584..d4d28258488 100644 --- a/sonar-server/src/main/java/org/sonar/server/db/migrations/violation/Referentials.java +++ b/sonar-server/src/main/java/org/sonar/server/db/migrations/violation/Referentials.java @@ -30,17 +30,20 @@ import javax.annotation.Nullable; import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; +import java.sql.Statement; import java.util.Map; -public class Referentials { +class Referentials { private final Database database; private final Map<Long, String> loginsByUserId; private final Map<Long, String> plansById; + private final int totalViolations; - public Referentials(Database database) throws SQLException { + Referentials(Database database) throws SQLException { this.database = database; loginsByUserId = selectLongString("select id,login from users"); plansById = selectLongString("select id,kee from action_plans"); + totalViolations = selectInt("select count(id) from rule_failures"); } @CheckForNull @@ -53,6 +56,10 @@ public class Referentials { return id != null ? loginsByUserId.get(id) : null; } + int totalViolations() { + return totalViolations; + } + private Map<Long, String> selectLongString(String sql) throws SQLException { Connection connection = database.getDataSource().getConnection(); try { @@ -70,4 +77,21 @@ public class Referentials { DbUtils.closeQuietly(connection); } } + + private int selectInt(String sql) throws SQLException { + Connection connection = database.getDataSource().getConnection(); + Statement stmt = null; + ResultSet rs = null; + try { + stmt = connection.createStatement(); + rs = stmt.executeQuery(sql); + if (rs.next()) { + return rs.getInt(1); + } + return 0; + } finally { + DbUtils.closeQuietly(connection, stmt, rs); + } + } + } diff --git a/sonar-server/src/main/java/org/sonar/server/db/migrations/violation/SqlUtil.java b/sonar-server/src/main/java/org/sonar/server/db/migrations/violation/SqlUtil.java index 26e40b0ab21..e6b6ffbb3a8 100644 --- a/sonar-server/src/main/java/org/sonar/server/db/migrations/violation/SqlUtil.java +++ b/sonar-server/src/main/java/org/sonar/server/db/migrations/violation/SqlUtil.java @@ -25,13 +25,13 @@ import javax.annotation.CheckForNull; import java.sql.ResultSet; import java.sql.SQLException; -public class SqlUtil { +class SqlUtil { private SqlUtil() { // only static methods } - public static void log(Logger logger, SQLException e) { + static void log(Logger logger, SQLException e) { SQLException next = e.getNextException(); while (next != null) { logger.error("SQL error: {}. Message: {}", next.getSQLState(), next.getMessage()); @@ -40,19 +40,19 @@ public class SqlUtil { } @CheckForNull - public static Long getLong(ResultSet rs, String columnName) throws SQLException { + static Long getLong(ResultSet rs, String columnName) throws SQLException { long l = rs.getLong(columnName); return rs.wasNull() ? null : l; } @CheckForNull - public static Double getDouble(ResultSet rs, String columnName) throws SQLException { + static Double getDouble(ResultSet rs, String columnName) throws SQLException { double d = rs.getDouble(columnName); return rs.wasNull() ? null : d; } @CheckForNull - public static Integer getInt(ResultSet rs, String columnName) throws SQLException { + static Integer getInt(ResultSet rs, String columnName) throws SQLException { int i = rs.getInt(columnName); return rs.wasNull() ? null : i; } diff --git a/sonar-server/src/main/java/org/sonar/server/db/migrations/violation/ViolationConverter.java b/sonar-server/src/main/java/org/sonar/server/db/migrations/violation/ViolationConverter.java index 73e417faafe..1d57602224c 100644 --- a/sonar-server/src/main/java/org/sonar/server/db/migrations/violation/ViolationConverter.java +++ b/sonar-server/src/main/java/org/sonar/server/db/migrations/violation/ViolationConverter.java @@ -82,11 +82,13 @@ class ViolationConverter implements Runnable { private final Database db; private final Object[] violationIds; private final Referentials referentials; + private final Progress progress; - public ViolationConverter(Referentials referentials, Database db, Object[] violationIds) { + ViolationConverter(Referentials referentials, Database db, Object[] violationIds, Progress progress) { this.referentials = referentials; this.db = db; this.violationIds = violationIds; + this.progress = progress; } @Override @@ -182,6 +184,7 @@ class ViolationConverter implements Runnable { writeConnection.commit(); insertComments(writeConnection, allComments); + progress.increment(rows.size()); } catch (SQLException e) { //TODO diff --git a/sonar-server/src/main/java/org/sonar/server/db/migrations/violation/ViolationConverters.java b/sonar-server/src/main/java/org/sonar/server/db/migrations/violation/ViolationConverters.java index 09278de5c59..a643fa7fdc9 100644 --- a/sonar-server/src/main/java/org/sonar/server/db/migrations/violation/ViolationConverters.java +++ b/sonar-server/src/main/java/org/sonar/server/db/migrations/violation/ViolationConverters.java @@ -21,30 +21,39 @@ package org.sonar.server.db.migrations.violation; import org.sonar.core.persistence.Database; +import java.util.Timer; import java.util.concurrent.*; -public class ViolationConverters { +class ViolationConverters { - static final int MAX_THREADS = 4; + static final int MAX_THREADS = 5; private final ExecutorService executorService; private final Database database; private final Referentials referentials; + private final Progress progress; + private final Timer timer; - public ViolationConverters(Database db, Referentials referentials) { + ViolationConverters(Database db, Referentials referentials) { this.database = db; this.referentials = referentials; + + this.progress = new Progress(referentials.totalViolations()); + timer = new Timer(Progress.THREAD_NAME); + timer.schedule(progress, Progress.DELAY_MS, Progress.DELAY_MS); + BlockingQueue<Runnable> blockingQueue = new ArrayBlockingQueue<Runnable>(MAX_THREADS); RejectedExecutionHandler rejectedExecutionHandler = new ThreadPoolExecutor.CallerRunsPolicy(); this.executorService = new ThreadPoolExecutor(0, MAX_THREADS, 5L, TimeUnit.SECONDS, blockingQueue, rejectedExecutionHandler); } void convert(Object[] violationIds) { - executorService.execute(new ViolationConverter(referentials, database, violationIds)); + executorService.execute(new ViolationConverter(referentials, database, violationIds, progress)); } void waitForFinished() throws InterruptedException { executorService.shutdown(); - executorService.awaitTermination(30L, TimeUnit.SECONDS); + executorService.awaitTermination(10L, TimeUnit.SECONDS); + timer.cancel(); } } diff --git a/sonar-server/src/main/java/org/sonar/server/db/migrations/violation/ViolationMigration.java b/sonar-server/src/main/java/org/sonar/server/db/migrations/violation/ViolationMigration.java index 249893e7a00..86a94ddef0c 100644 --- a/sonar-server/src/main/java/org/sonar/server/db/migrations/violation/ViolationMigration.java +++ b/sonar-server/src/main/java/org/sonar/server/db/migrations/violation/ViolationMigration.java @@ -31,7 +31,6 @@ import org.sonar.server.db.DatabaseMigration; import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; -import java.util.Arrays; /** * Used in the Active Record Migration 401 @@ -78,14 +77,16 @@ public class ViolationMigration implements DatabaseMigration { public void migrate(Database db) throws Exception { Referentials referentials = new Referentials(db); - ViolationConverters converters = new ViolationConverters(db, referentials); - Connection readConnection = db.getDataSource().getConnection(); - try { - new QueryRunner().query(readConnection, "select id from rule_failures", new ViolationIdHandler(converters)); - } finally { - DbUtils.closeQuietly(readConnection); + if (referentials.totalViolations() > 0) { + ViolationConverters converters = new ViolationConverters(db, referentials); + Connection readConnection = db.getDataSource().getConnection(); + try { + new QueryRunner().query(readConnection, "select id from rule_failures", new ViolationIdHandler(converters)); + } finally { + DbUtils.closeQuietly(readConnection); + } + converters.waitForFinished(); } - converters.waitForFinished(); } private static class ViolationIdHandler implements ResultSetHandler { @@ -113,9 +114,9 @@ public class ViolationMigration implements DatabaseMigration { total++; } if (cursor > 0) { - for (int i=0 ; i<violationIds.length ; i++) { - if (violationIds[i]==null) { - violationIds[i]=-1; + for (int i = 0; i < violationIds.length; i++) { + if (violationIds[i] == null) { + violationIds[i] = -1; } } converters.convert(violationIds); diff --git a/sonar-server/src/test/java/org/sonar/server/db/migrations/violation/ProgressTest.java b/sonar-server/src/test/java/org/sonar/server/db/migrations/violation/ProgressTest.java new file mode 100644 index 00000000000..1e437c9794d --- /dev/null +++ b/sonar-server/src/test/java/org/sonar/server/db/migrations/violation/ProgressTest.java @@ -0,0 +1,50 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2013 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.server.db.migrations.violation; + +import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.slf4j.Logger; + +import static org.fest.assertions.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +public class ProgressTest { + @Test + public void log_progress() throws Exception { + Logger logger = mock(Logger.class); + ArgumentCaptor<String> argument = ArgumentCaptor.forClass(String.class); + + Progress progress = new Progress(5000, logger); + progress.run(); + progress.increment(200); + progress.increment(130); + progress.run(); + progress.increment(1670); + progress.run(); + + verify(logger, times(3)).info(argument.capture()); + assertThat(argument.getAllValues().get(0)).matches("0% \\[0/5000 violations, 0 violations/minute, \\d+ minutes remaining\\]"); + assertThat(argument.getAllValues().get(1)).matches("6% \\[330/5000 violations, \\d+ violations/minute, \\d+ minutes remaining\\]"); + assertThat(argument.getAllValues().get(2)).matches("40% \\[2000/5000 violations, \\d+ violations/minute, \\d+ minutes remaining\\]"); + } +} diff --git a/sonar-server/src/test/java/org/sonar/server/db/migrations/violation/ViolationMigrationTest.java b/sonar-server/src/test/java/org/sonar/server/db/migrations/violation/ViolationMigrationTest.java index 67e4f8cd52c..58e96d25e8b 100644 --- a/sonar-server/src/test/java/org/sonar/server/db/migrations/violation/ViolationMigrationTest.java +++ b/sonar-server/src/test/java/org/sonar/server/db/migrations/violation/ViolationMigrationTest.java @@ -23,6 +23,10 @@ import org.junit.Rule; import org.junit.Test; import org.sonar.core.persistence.TestDatabase; +import java.util.Set; + +import static org.fest.assertions.Assertions.assertThat; + public class ViolationMigrationTest { @Rule @@ -34,7 +38,12 @@ public class ViolationMigrationTest { new ViolationMigration().execute(db.database()); - //Thread.sleep(5000L); db.assertDbUnit(getClass(), "migrate_violations_result.xml", "issues", "issue_changes"); + + // Progress thread is dead + Set<Thread> threads = Thread.getAllStackTraces().keySet(); + for (Thread thread : threads) { + assertThat(thread.getName()).isNotEqualTo(Progress.THREAD_NAME); + } } } |