]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-20058 improve webhook purge mechanism
authorPierre <pierre.guillot@sonarsource.com>
Tue, 29 Aug 2023 07:03:58 +0000 (09:03 +0200)
committersonartech <sonartech@sonarsource.com>
Thu, 31 Aug 2023 20:02:56 +0000 (20:02 +0000)
14 files changed:
server/sonar-db-dao/src/it/java/org/sonar/db/webhook/WebhookDeliveryDaoIT.java
server/sonar-db-dao/src/main/java/org/sonar/db/webhook/WebhookDeliveryDao.java
server/sonar-db-dao/src/main/java/org/sonar/db/webhook/WebhookDeliveryMapper.java
server/sonar-db-dao/src/main/resources/org/sonar/db/webhook/WebhookDeliveryMapper.xml
server/sonar-db-dao/src/schema/schema-sq.ddl
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v102/CreateIndexCreatedAtInWebhookDeliveries.java [new file with mode: 0644]
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v102/DbVersion102.java
server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v102/CreateIndexCreatedAtInWebhookDeliveriesTest.java [new file with mode: 0644]
server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v102/CreateIndexCreatedAtInWebhookDeliveriesTest/schema.sql [new file with mode: 0644]
server/sonar-server-common/src/it/java/org/sonar/server/webhook/AsynchronousWebHooksImplIT.java
server/sonar-server-common/src/it/java/org/sonar/server/webhook/SynchronousWebHooksImplIT.java
server/sonar-server-common/src/it/java/org/sonar/server/webhook/WebhookDeliveryStorageIT.java
server/sonar-server-common/src/main/java/org/sonar/server/webhook/WebHooksImpl.java
server/sonar-server-common/src/main/java/org/sonar/server/webhook/WebhookDeliveryStorage.java

index 5f0d6214b6274f6e8b7c3224b68a8b674c252c28..abce0ac88089fcebfd11c696f95d79efedb8998e 100644 (file)
@@ -208,32 +208,24 @@ public class WebhookDeliveryDaoIT {
   @Test
   public void deleteComponentBeforeDate_deletes_rows_before_date() {
     underTest.insert(dbSession, WebhookDeliveryTesting.newDto("DELIVERY_1", "WEBHOOK_UUID_1", "PROJECT_1", "TASK_1").setCreatedAt(1_000_000L));
-    underTest.insert(dbSession, WebhookDeliveryTesting.newDto("DELIVERY_2", "WEBHOOK_UUID_1", "PROJECT_1", "TASK_2").setCreatedAt(2_000_000L));
-    underTest.insert(dbSession, WebhookDeliveryTesting.newDto("DELIVERY_3", "WEBHOOK_UUID_1", "PROJECT_2", "TASK_3").setCreatedAt(1_000_000L));
+    underTest.insert(dbSession, WebhookDeliveryTesting.newDto("DELIVERY_2", "WEBHOOK_UUID_2", "PROJECT_1", "TASK_2").setCreatedAt(2_000_000L));
+    underTest.insert(dbSession, WebhookDeliveryTesting.newDto("DELIVERY_3", "WEBHOOK_UUID_3", "PROJECT_2", "TASK_3").setCreatedAt(1_000_000L));
+    underTest.insert(dbSession, WebhookDeliveryTesting.newDto("DELIVERY_4", "WEBHOOK_UUID_4", "PROJECT_3", "TASK_4").setCreatedAt(2_000_000L));
 
     // should delete the old delivery on PROJECT_1 and keep the one of PROJECT_2
-    underTest.deleteProjectBeforeDate(dbSession, "PROJECT_1", 1_500_000L);
+    underTest.deleteAllBeforeDate(dbSession, 1_500_000L);
 
     List<Map<String, Object>> uuids = dbTester.select(dbSession, "select uuid as \"uuid\" from webhook_deliveries");
-    assertThat(uuids).extracting(column -> column.get("uuid")).containsOnly("DELIVERY_2", "DELIVERY_3");
+    assertThat(uuids).extracting(column -> column.get("uuid")).containsOnly("DELIVERY_2", "DELIVERY_4");
   }
 
   @Test
   public void deleteComponentBeforeDate_does_nothing_on_empty_table() {
-    underTest.deleteProjectBeforeDate(dbSession, "PROJECT_1", 1_500_000L);
+    underTest.deleteAllBeforeDate(dbSession, 1_500_000L);
 
     assertThat(dbTester.countRowsOfTable(dbSession, "webhook_deliveries")).isZero();
   }
 
-  @Test
-  public void deleteComponentBeforeDate_does_nothing_on_invalid_uuid() {
-    underTest.insert(dbSession, WebhookDeliveryTesting.newDto("DELIVERY_1", "WEBHOOK_UUID_1", "PROJECT_1", "TASK_1").setCreatedAt(1_000_000L));
-
-    underTest.deleteProjectBeforeDate(dbSession, "PROJECT_2", 1_500_000L);
-
-    assertThat(dbTester.countRowsOfTable(dbSession, "webhook_deliveries")).isOne();
-  }
-
   private void verifyMandatoryFields(WebhookDeliveryDto expected, WebhookDeliveryDto actual) {
     assertThat(actual.getUuid()).isEqualTo(expected.getUuid());
     assertThat(actual.getProjectUuid()).isEqualTo(expected.getProjectUuid());
index 565ad0deb7e4f612892cba329a73b06c7c5f0e0c..d5819ba26bad89a5a5ac4e2b4f70fbd0b856fa4b 100644 (file)
@@ -72,8 +72,8 @@ public class WebhookDeliveryDao implements Dao {
     mapper(dbSession).insert(dto);
   }
 
-  public void deleteProjectBeforeDate(DbSession dbSession, String projectUuid, long beforeDate) {
-    mapper(dbSession).deleteProjectBeforeDate(projectUuid, beforeDate);
+  public void deleteAllBeforeDate(DbSession dbSession, long beforeDate) {
+    mapper(dbSession).deleteAllBeforeDate(beforeDate);
   }
 
   public Map<String, WebhookDeliveryLiteDto> selectLatestDeliveries(DbSession dbSession, List<WebhookDto> webhooks) {
index 7a1764f78d144db019d330dfd9b4bb719357d662..acc8a1065fbed2ef5f2c59f01dc1d5819136950b 100644 (file)
@@ -43,7 +43,7 @@ public interface WebhookDeliveryMapper {
 
   void insert(WebhookDeliveryDto dto);
 
-  void deleteProjectBeforeDate(@Param("projectUuid") String projectUuid, @Param("beforeDate") long beforeDate);
+  void deleteAllBeforeDate(@Param("beforeDate") long beforeDate);
 
   void deleteByWebhookUuid(@Param("webhookUuid") String webhookUuid);
 }
index 62796241a06ba8830c994d9e696f1134eef56284..fa7a2e090337f0898e8f03f2204bb156e871856f 100644 (file)
     where webhook_uuid = #{webhookUuid,jdbcType=VARCHAR}
   </select>
 
-  <delete id="deleteProjectBeforeDate" parameterType="map">
+  <delete id="deleteAllBeforeDate" parameterType="map">
     delete from webhook_deliveries
     where
-    project_uuid = #{projectUuid,jdbcType=VARCHAR} and
     created_at &lt; #{beforeDate,jdbcType=BIGINT}
   </delete>
 </mapper>
index ed3bc1a8d30ef4939b7549cd8242c8b992a3608b..e5cbfa2acde1a90a95c33fc9e7ddfcb5dc3330f9 100644 (file)
@@ -1105,6 +1105,7 @@ ALTER TABLE "WEBHOOK_DELIVERIES" ADD CONSTRAINT "PK_WEBHOOK_DELIVERIES" PRIMARY
 CREATE INDEX "WD_WEBHOOK_UUID_CREATED_AT" ON "WEBHOOK_DELIVERIES"("WEBHOOK_UUID" NULLS FIRST, "CREATED_AT" NULLS FIRST);
 CREATE INDEX "WD_PROJECT_UUID_CREATED_AT" ON "WEBHOOK_DELIVERIES"("PROJECT_UUID" NULLS FIRST, "CREATED_AT" NULLS FIRST);
 CREATE INDEX "WD_CE_TASK_UUID_CREATED_AT" ON "WEBHOOK_DELIVERIES"("CE_TASK_UUID" NULLS FIRST, "CREATED_AT" NULLS FIRST);
+CREATE INDEX "WD_CREATED_AT" ON "WEBHOOK_DELIVERIES"("CREATED_AT" NULLS FIRST);
 
 CREATE TABLE "WEBHOOKS"(
     "UUID" CHARACTER VARYING(40) NOT NULL,
diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v102/CreateIndexCreatedAtInWebhookDeliveries.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v102/CreateIndexCreatedAtInWebhookDeliveries.java
new file mode 100644 (file)
index 0000000..c3a9751
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program 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.
+ *
+ * This program 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.platform.db.migration.version.v102;
+
+import org.sonar.db.Database;
+import org.sonar.server.platform.db.migration.step.CreateIndexOnColumns;
+
+public class CreateIndexCreatedAtInWebhookDeliveries extends CreateIndexOnColumns {
+  protected CreateIndexCreatedAtInWebhookDeliveries(Database db) {
+    super(db, "webhook_deliveries", "wd", false, "created_at");
+  }
+}
index 7cb2b645ef1f91df11fd27c0f8529b61bdb2b121..66b80b60b0b81a3d01445f4796922c5becdbd029 100644 (file)
@@ -109,6 +109,8 @@ public class DbVersion102 implements DbVersion {
       .add(10_2_050, "Create index 'wd_project_uuid_created_at' in 'webhook_deliveries'", CreateIndexProjectUuidCreatedAtInWebhookDeliveries.class)
       .add(10_2_051, "Drop index 'ce_task_uuid' in 'webhook_deliveries'", DropIndexTaskUuidInWebhookDeliveries.class)
       .add(10_2_052, "Create index 'wd_task_uuid_created_at' in 'webhook_deliveries'", CreateIndexTaskUuidCreatedAtInWebhookDeliveries.class)
+      .add(10_2_053, "Create index 'wd_created_at' in 'webhook_deliveries'", CreateIndexCreatedAtInWebhookDeliveries.class)
+
     ;
   }
 }
diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v102/CreateIndexCreatedAtInWebhookDeliveriesTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v102/CreateIndexCreatedAtInWebhookDeliveriesTest.java
new file mode 100644 (file)
index 0000000..2a62cf3
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program 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.
+ *
+ * This program 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.platform.db.migration.version.v102;
+
+import java.sql.SQLException;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.db.CoreDbTester;
+import org.sonar.server.platform.db.migration.step.DdlChange;
+
+public class CreateIndexCreatedAtInWebhookDeliveriesTest {
+
+  public static final String TABLE_NAME = "webhook_deliveries";
+  public static final String INDEX_NAME = "wd_created_at";
+  public static final String EXPECTED_COLUMN = "created_at";
+  @Rule
+  public final CoreDbTester db = CoreDbTester.createForSchema(CreateIndexCreatedAtInWebhookDeliveriesTest.class, "schema.sql");
+
+  private final DdlChange createIndex = new CreateIndexCreatedAtInWebhookDeliveries(db.database());
+
+  @Test
+  public void migration_should_create_index() throws SQLException {
+    db.assertIndexDoesNotExist(TABLE_NAME, INDEX_NAME);
+
+    createIndex.execute();
+
+    db.assertIndex(TABLE_NAME, INDEX_NAME, EXPECTED_COLUMN);
+  }
+
+  @Test
+  public void migration_should_be_reentrant() throws SQLException {
+    createIndex.execute();
+    createIndex.execute();
+
+    db.assertIndex(TABLE_NAME, INDEX_NAME, EXPECTED_COLUMN);
+  }
+
+}
diff --git a/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v102/CreateIndexCreatedAtInWebhookDeliveriesTest/schema.sql b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v102/CreateIndexCreatedAtInWebhookDeliveriesTest/schema.sql
new file mode 100644 (file)
index 0000000..f5cef78
--- /dev/null
@@ -0,0 +1,19 @@
+CREATE TABLE "WEBHOOK_DELIVERIES"(
+    "UUID" CHARACTER VARYING(40) NOT NULL,
+    "WEBHOOK_UUID" CHARACTER VARYING(40) NOT NULL,
+    "PROJECT_UUID" CHARACTER VARYING(40) NOT NULL,
+    "CE_TASK_UUID" CHARACTER VARYING(40),
+    "ANALYSIS_UUID" CHARACTER VARYING(40),
+    "NAME" CHARACTER VARYING(100) NOT NULL,
+    "URL" CHARACTER VARYING(2000) NOT NULL,
+    "SUCCESS" BOOLEAN NOT NULL,
+    "HTTP_STATUS" INTEGER,
+    "DURATION_MS" BIGINT NOT NULL,
+    "PAYLOAD" CHARACTER LARGE OBJECT NOT NULL,
+    "ERROR_STACKTRACE" CHARACTER LARGE OBJECT,
+    "CREATED_AT" BIGINT NOT NULL
+);
+ALTER TABLE "WEBHOOK_DELIVERIES" ADD CONSTRAINT "PK_WEBHOOK_DELIVERIES" PRIMARY KEY("UUID");
+CREATE INDEX "WD_WEBHOOK_UUID_CREATED_AT" ON "WEBHOOK_DELIVERIES"("WEBHOOK_UUID", "CREATED_AT" NULLS FIRST);
+CREATE INDEX "WD_PROJECT_UUID_CREATED_AT" ON "WEBHOOK_DELIVERIES"("PROJECT_UUID", "CREATED_AT" NULLS FIRST);
+CREATE INDEX "WD_CE_TASK_UUID_CREATED_AT" ON "WEBHOOK_DELIVERIES"("CE_TASK_UUID", "CREATED_AT" NULLS FIRST);
index e82990ec535d59dad1f13382d6d5a1a5cf869c25..03ba7c1ae38cf46fc8c917682927480fd1c2d524 100644 (file)
@@ -78,7 +78,7 @@ public class AsynchronousWebHooksImplIT {
 
     assertThat(caller.countSent()).isEqualTo(2);
     verify(deliveryStorage, times(2)).persist(any(WebhookDelivery.class));
-    verify(deliveryStorage).purge(project.getUuid());
+    verify(deliveryStorage).purge();
   }
 
   private static class RecordingAsyncExecution implements AsyncExecution {
index 25efc4b62ba04ea035dd0d7625143d19d679032e..7f3cde4d6e89008bc3732139096769e5e1c1d5cc 100644 (file)
@@ -130,7 +130,7 @@ public class SynchronousWebHooksImplIT {
     assertThat(logTester.logs(DEBUG)).contains("Sent webhook 'First' | url=http://url1 | time=1234ms | status=200");
     assertThat(logTester.logs(DEBUG)).contains("Failed to send webhook 'Second' | url=http://url2 | message=Fail to connect");
     verify(deliveryStorage, times(2)).persist(any(WebhookDelivery.class));
-    verify(deliveryStorage).purge(projectDto.getUuid());
+    verify(deliveryStorage).purge();
     verifyLogStatistics(2, 0);
   }
 
@@ -151,7 +151,7 @@ public class SynchronousWebHooksImplIT {
     assertThat(caller.countSent()).isOne();
     assertThat(logTester.logs(DEBUG)).contains("Sent webhook 'First' | url=http://url1 | time=1234ms | status=200");
     verify(deliveryStorage).persist(any(WebhookDelivery.class));
-    verify(deliveryStorage).purge(projectDto.getUuid());
+    verify(deliveryStorage).purge();
     verifyLogStatistics(0, 1);
   }
 
@@ -180,7 +180,7 @@ public class SynchronousWebHooksImplIT {
       .contains("Sent webhook '4Fourth' | url=http://url4 | time=5678ms | status=200")
       .contains("Sent webhook '5Fifth' | url=http://url5 | time=9256ms | status=200");
     verify(deliveryStorage, times(5)).persist(any(WebhookDelivery.class));
-    verify(deliveryStorage).purge(projectDto.getUuid());
+    verify(deliveryStorage).purge();
     verifyLogStatistics(3, 2);
   }
 
index e7bde6cbf023507f68a62b814a3c7d226d64a53a..f4b2efdea536167414eb52eef96c31293ec9e60e 100644 (file)
@@ -89,17 +89,17 @@ public class WebhookDeliveryStorageIT {
   }
 
   @Test
-  public void purge_deletes_records_older_than_one_month_on_the_project() {
+  public void purge_deletes_records_older_than_one_month() {
     when(system.now()).thenReturn(NOW);
     dbClient.webhookDeliveryDao().insert(dbSession, newDto("D1", "PROJECT_1", TWO_MONTHS_AGO));
     dbClient.webhookDeliveryDao().insert(dbSession, newDto("D2", "PROJECT_1", TWO_WEEKS_AGO));
     dbClient.webhookDeliveryDao().insert(dbSession, newDto("D3", "PROJECT_2", TWO_MONTHS_AGO));
+    dbClient.webhookDeliveryDao().insert(dbSession, newDto("D4", "PROJECT_2", TWO_WEEKS_AGO));
     dbSession.commit();
 
-    underTest.purge("PROJECT_1");
+    underTest.purge();
 
-    // do not purge another project PROJECT_2
-    assertThat(selectAllDeliveryUuids(dbTester, dbSession)).containsOnly("D2", "D3");
+    assertThat(selectAllDeliveryUuids(dbTester, dbSession)).containsOnly("D2", "D4");
   }
 
   @Test
index f837ce520f6b783bdcf31b2618bcba56e66df3ac..a74855d8c54e89827371499e01ae212ce5f67cd3 100644 (file)
@@ -116,7 +116,7 @@ public class WebHooksImpl implements WebHooks {
       log(delivery);
       deliveryStorage.persist(delivery);
     }));
-    asyncExecution.addToQueue(() -> deliveryStorage.purge(analysis.projectUuid()));
+    asyncExecution.addToQueue(deliveryStorage::purge);
   }
 
   private static void log(WebhookDelivery delivery) {
index 4a0182e84e84f39b76ed208bc9831b578e9b866f..ad2bd3294e220b7f9977adada30c4c3ebb61bb7f 100644 (file)
@@ -56,10 +56,10 @@ public class WebhookDeliveryStorage {
     }
   }
 
-  public void purge(String projectUuid) {
+  public void purge() {
     long beforeDate = system.now() - ALIVE_DELAY_MS;
     try (DbSession dbSession = dbClient.openSession(false)) {
-      dbClient.webhookDeliveryDao().deleteProjectBeforeDate(dbSession, projectUuid, beforeDate);
+      dbClient.webhookDeliveryDao().deleteAllBeforeDate(dbSession, beforeDate);
       dbSession.commit();
     }
   }