]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-4690 log progress status
authorSimon Brandhof <simon.brandhof@gmail.com>
Fri, 20 Sep 2013 11:17:15 +0000 (13:17 +0200)
committerSimon Brandhof <simon.brandhof@gmail.com>
Fri, 20 Sep 2013 11:17:25 +0000 (13:17 +0200)
sonar-server/src/main/java/org/sonar/server/db/migrations/violation/Progress.java [new file with mode: 0644]
sonar-server/src/main/java/org/sonar/server/db/migrations/violation/Referentials.java
sonar-server/src/main/java/org/sonar/server/db/migrations/violation/SqlUtil.java
sonar-server/src/main/java/org/sonar/server/db/migrations/violation/ViolationConverter.java
sonar-server/src/main/java/org/sonar/server/db/migrations/violation/ViolationConverters.java
sonar-server/src/main/java/org/sonar/server/db/migrations/violation/ViolationMigration.java
sonar-server/src/test/java/org/sonar/server/db/migrations/violation/ProgressTest.java [new file with mode: 0644]
sonar-server/src/test/java/org/sonar/server/db/migrations/violation/ViolationMigrationTest.java

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 (file)
index 0000000..fe1175a
--- /dev/null
@@ -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)
+    );
+  }
+}
index 43e14fa358483c6e2f51dd776bee3d45e6af95c8..d4d28258488ec7f88f80df397e5c8e99134b5c5d 100644 (file)
@@ -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);
+    }
+  }
+
 }
index 26e40b0ab21cfbf2262f0f7e01bc446b6b2de0c6..e6b6ffbb3a8cacf22221cbb451e8d92ae8850321 100644 (file)
@@ -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;
   }
index 73e417faafea5cc92237ad1c666879d90b416f0c..1d57602224c6e010512bb6a1fb27e005400212a2 100644 (file)
@@ -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
index 09278de5c59ce83ec0d6a3ccb17a6985501c7891..a643fa7fdc91faa1f94619b6d7122216be50cf28 100644 (file)
@@ -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();
   }
 }
index 249893e7a00f22ac677d2091087c1922e230cfd8..86a94ddef0cfced4a6609543ce7871ff9cff9748 100644 (file)
@@ -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 (file)
index 0000000..1e437c9
--- /dev/null
@@ -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\\]");
+  }
+}
index 67e4f8cd52ca3d898a050b18036328b9ffe2e2e0..58e96d25e8b0c14e581cb4c51de656928b89d99d 100644 (file)
@@ -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);
+    }
   }
 }