]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-14297 Make migration app xml->db re-entrant in case failure
authorJacek <jacek.poreda@sonarsource.com>
Tue, 22 Dec 2020 12:03:08 +0000 (13:03 +0100)
committersonartech <sonartech@sonarsource.com>
Wed, 23 Dec 2020 20:10:14 +0000 (20:10 +0000)
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v86/MigrateApplicationDefinitionsFromXmlToDb.java
server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v86/MigrateApplicationDefinitionsFromXmlToDbTest.java

index 62257488f0f994dfb491ab5512e90f037db30b16..7dff6036a36f889766b2c2668081e640a2ee6424 100644 (file)
@@ -74,6 +74,7 @@ public class MigrateApplicationDefinitionsFromXmlToDb extends DataChange {
   static final int TEXT_VALUE_MAX_LENGTH = 4000;
   private static final String SELECT_APPLICATION_UUID_BY_KEY = "select uuid from projects where kee = ? and qualifier = 'APP'";
   private static final String SELECT_PROJECTS_BY_KEYS = "select kee,uuid from projects where kee in (%s) and qualifier = 'TRK'";
+  private static final String SELECT_PROJECTS_BY_APP = "select project_uuid from app_projects where application_uuid = ?";
   private static final String UPDATE_INTERNAL_PROP_TEXT_VALUE = "update internal_properties set text_value = ?, clob_value = NULL where kee = ?";
   private static final String UPDATE_INTERNAL_PROP_CLOB_VALUE = "update internal_properties set clob_value = ?, text_value = NULL where kee = ?";
   private static final String VIEWS_DEF_KEY = "views.def";
@@ -126,13 +127,19 @@ public class MigrateApplicationDefinitionsFromXmlToDb extends DataChange {
       return;
     }
 
+    List<String> alreadyAddedProjects = context.prepareSelect(SELECT_PROJECTS_BY_APP).setString(1, applicationUuid)
+      .list(r -> r.getString(1));
+
     String queryParam = projects.stream().map(uuid -> "'" + uuid + "'").collect(Collectors.joining(","));
     Map<String, String> projectUuidsByKeys = context.prepareSelect(format(SELECT_PROJECTS_BY_KEYS, queryParam))
       .list(r -> new AbstractMap.SimpleEntry<>(r.getString(1), r.getString(2)))
       .stream()
+      .filter(project -> !alreadyAddedProjects.contains(project.getValue()))
       .collect(Collectors.toMap(AbstractMap.SimpleEntry::getKey, AbstractMap.SimpleEntry::getValue));
 
-    insertApplicationProjects(context, app, applicationUuid, projectUuidsByKeys, now);
+    if (!projectUuidsByKeys.isEmpty()) {
+      insertApplicationProjects(context, app, applicationUuid, projectUuidsByKeys, now);
+    }
     if (!app.getApplicationBranches().isEmpty()) {
       insertApplicationBranchesProjects(context, app, applicationUuid, projectUuidsByKeys, now);
     }
index 856943d06443aeaed22f1f2c97f35a2b9aeb5622..04d30fd5478887216fdcd733a747e379397b0631 100644 (file)
@@ -433,6 +433,41 @@ public class MigrateApplicationDefinitionsFromXmlToDbTest {
         tuple(APP_2_UUID, PROJECT_2_UUID, APP_2_BRANCH_1_UUID, PROJECT_2_BRANCH_1_UUID));
   }
 
+  @Test
+  public void migration_is_resilient() throws SQLException {
+    setupProjectsAndApps();
+    insertViewsDefInternalProperty(COMPLEX_XML_BEFORE);
+
+    // first attempt
+    underTest.execute();
+
+    // xml stays the same (stopped during migration)
+    updateViewsDefInternalProperty(COMPLEX_XML_BEFORE);
+
+    // second attempt should not fail
+    underTest.execute();
+
+    assertThat(db.select("select uuid from projects"))
+      .extracting(r -> r.get("UUID"))
+      .containsExactlyInAnyOrder(PROJECT_1_UUID, PROJECT_2_UUID, APP_1_UUID, APP_2_UUID);
+    assertThat(db.select("select application_uuid, project_uuid from app_projects"))
+      .extracting(r -> r.get("APPLICATION_UUID"), r -> r.get("PROJECT_UUID"))
+      .containsExactlyInAnyOrder(
+        tuple(APP_1_UUID, PROJECT_1_UUID),
+        tuple(APP_1_UUID, PROJECT_2_UUID),
+        tuple(APP_2_UUID, PROJECT_1_UUID),
+        tuple(APP_2_UUID, PROJECT_2_UUID));
+    assertThat(db.select("select application_uuid, project_uuid, application_branch_uuid, project_branch_uuid from app_branch_project_branch"))
+      .extracting(r -> r.get("APPLICATION_UUID"), r -> r.get("PROJECT_UUID"), r -> r.get("APPLICATION_BRANCH_UUID"), r -> r.get("PROJECT_BRANCH_UUID"))
+      .containsExactlyInAnyOrder(
+        tuple(APP_1_UUID, PROJECT_1_UUID, APP_1_BRANCH_1_UUID, PROJECT_1_BRANCH_1_UUID),
+        tuple(APP_1_UUID, PROJECT_2_UUID, APP_1_BRANCH_1_UUID, PROJECT_2_BRANCH_1_UUID),
+        tuple(APP_1_UUID, PROJECT_1_UUID, APP_1_BRANCH_2_UUID, PROJECT_1_BRANCH_2_UUID),
+        tuple(APP_1_UUID, PROJECT_2_UUID, APP_1_BRANCH_2_UUID, PROJECT_2_BRANCH_1_UUID),
+        tuple(APP_2_UUID, PROJECT_1_UUID, APP_2_BRANCH_1_UUID, PROJECT_1_BRANCH_1_UUID),
+        tuple(APP_2_UUID, PROJECT_2_UUID, APP_2_BRANCH_1_UUID, PROJECT_2_BRANCH_1_UUID));
+  }
+
   @Test
   public void migrates_applications_without_application_branches_to_new_tables() throws SQLException {
     setupFullProject1();
@@ -681,6 +716,11 @@ public class MigrateApplicationDefinitionsFromXmlToDbTest {
       "created_at", system2.now());
   }
 
+  private void updateViewsDefInternalProperty(@Nullable String xml) {
+    db.executeUpdateSql("update internal_properties set text_value = ? where kee = 'views.def'",
+      xml);
+  }
+
   private void insertProject(String uuid, String key, String qualifier) {
     db.executeInsert("PROJECTS",
       "UUID", uuid,