]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-12251 detect aborted attempt during processing of 1st project
authorSébastien Lesaint <sebastien.lesaint@sonarsource.com>
Thu, 4 Jul 2019 08:14:24 +0000 (10:14 +0200)
committerSonarTech <sonartech@sonarsource.com>
Tue, 30 Jul 2019 18:24:26 +0000 (20:24 +0200)
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/charset/ColumnDef.java
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v70/PopulateLiveMeasures.java
server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v70/PopulateLiveMeasuresTest.java

index 386ed39ce0904afbcb04f76bb5aa110f6fa0f14f..134f3b5e90fffb47683b173e19f0de64c843ad32 100644 (file)
@@ -90,7 +90,7 @@ public class ColumnDef {
     @Override
     public ColumnDef convert(ResultSet rs) throws SQLException {
       String nullableText = rs.getString(7);
-      boolean nullable = "YES".equalsIgnoreCase(nullableText);
+      boolean nullable = "FIRST".equalsIgnoreCase(nullableText);
 
       return new ColumnDef(
         rs.getString(1), rs.getString(2), rs.getString(3), rs.getString(4), rs.getString(5), rs.getLong(6), nullable);
index f82d1ea67409e3e8ffc17d9c9bec4406bfa553ff..76405c389eb962552c2b2257afc665ce2332de23 100644 (file)
@@ -54,9 +54,9 @@ public class PopulateLiveMeasures extends DataChange {
 
   @Override
   protected void execute(Context context) throws SQLException {
-    boolean firstAttempt = isFirstAttempt(context);
-    if (!firstAttempt) {
-      LOG.info("Retry detected (non empty table live_measures_p). Handle it");
+    ATTEMPT attempt = isFirstAttempt(context);
+    if (attempt != ATTEMPT.FIRST) {
+      LOG.info("Retry detected (non empty table live_measures_p or live_measures). Handling it");
     }
 
     long now = system2.now();
@@ -67,32 +67,44 @@ public class PopulateLiveMeasures extends DataChange {
       " from snapshots s" +
       " where" +
       "   s.islast = ?" +
-      (firstAttempt ? "" : "   and not exists (select 1 from live_measures_p lmp where lmp.project_uuid=s.component_uuid)");
+      (attempt == ATTEMPT.ONE_PROJECT_OR_MORE ? "   and not exists (select 1 from live_measures_p lmp where lmp.project_uuid=s.component_uuid)" : "");
     try (Connection connection = createReadUncommittedConnection();
       Select select = SelectImpl.create(db, connection, statement).setBoolean(1, true)) {
       List<Row> rows = new ArrayList<>(projectBatchSize);
       select.scroll(t -> {
         rows.add(new Row(t.getString(1), t.getString(2)));
         if (rows.size() == projectBatchSize) {
-          processProjectBatch(context, rows, firstAttempt, now);
+          processProjectBatch(context, rows, attempt, now);
 
           rows.clear();
         }
       });
 
       if (!rows.isEmpty()) {
-        processProjectBatch(context, rows, firstAttempt, now);
+        processProjectBatch(context, rows, attempt, now);
       }
     }
   }
 
-  private static boolean isFirstAttempt(Context context) throws SQLException {
-    try (Select select = context.prepareSelect("select count(1) from live_measures_p")) {
-      return select.get(t -> t.getLong(1)) == 0;
+  private static ATTEMPT isFirstAttempt(Context context) throws SQLException {
+    try (
+      Select selectFromTempTable = context.prepareSelect("select count(1) from live_measures_p");
+      Select selectFromLiveMeasures = context.prepareSelect("select count(1) from live_measures")
+    ) {
+      boolean projectsProcessed = selectFromTempTable.get(t -> t.getLong(1)) > 0;
+      if (projectsProcessed) {
+        return ATTEMPT.ONE_PROJECT_OR_MORE;
+      }
+      // we will count at most the data of one project => table should have little content => bad performance risk is low
+      return selectFromLiveMeasures.get(t -> t.getLong(1)) == 0 ? ATTEMPT.FIRST : ATTEMPT.PARTIAL;
     }
   }
 
-  private static void processProjectBatch(Context context, List<Row> rows, boolean firstAttempt, long now) throws SQLException {
+  private enum ATTEMPT {
+    FIRST, PARTIAL, ONE_PROJECT_OR_MORE
+  }
+
+  private static void processProjectBatch(Context context, List<Row> rows, ATTEMPT attempt, long now) throws SQLException {
     MassUpdate massUpdate = context.prepareMassUpdate();
     massUpdate.rowPluralName("live measures");
     setSelect(rows, massUpdate);
@@ -104,7 +116,7 @@ public class PopulateLiveMeasures extends DataChange {
     massUpdate.update("insert into live_measures_p (project_uuid) values (?)")
       // we want to commit each project finished asap to avoid restarting them over in case of interruption
       .setBatchSize(1);
-    LiveMeasurePopulationMultiHandler handler = new LiveMeasurePopulationMultiHandler(firstAttempt, rows, now);
+    LiveMeasurePopulationMultiHandler handler = new LiveMeasurePopulationMultiHandler(attempt == ATTEMPT.FIRST, rows, now);
     massUpdate.execute(handler);
 
     Set<String> notCommittedProjectUuids = handler.notCommittedProjectUuids;
index 6368c94bfaf8c300116f892b9c4c598295a88d47..e25e9e8410c521d507092345ebcca4cad9c6709b 100644 (file)
@@ -87,7 +87,7 @@ public class PopulateLiveMeasuresTest {
   }
 
   @Test
-  public void do_not_fail_if_live_measure_of_component_already_partially_inserted() throws SQLException {
+  public void migration_is_reentrant_on_partially_processed_1st_project() throws SQLException {
     generateProjectMeasures();
 
     db.executeInsert(
@@ -102,6 +102,14 @@ public class PopulateLiveMeasuresTest {
 
     underTest.execute();
 
+    assertThat(getLiveMeasures()).extracting(
+      field("COMPONENT_UUID"),
+      field("PROJECT_UUID"),
+      field("METRIC_ID"),
+      field("VALUE"),
+      field("TEXT_VALUE"),
+      field("VARIATION"),
+      field("MEASURE_DATA")).containsExactlyInAnyOrder(generateLiveMeasures());
   }
 
   private Function<Map<String, Object>, Object> field(String name) {