diff --git a/dotCMS/src/main/java/com/dotmarketing/startup/runonce/Task241009CreatePostgresJobQueueTables.java b/dotCMS/src/main/java/com/dotmarketing/startup/runonce/Task241009CreatePostgresJobQueueTables.java index c75b274b1d8..df1d33198c2 100644 --- a/dotCMS/src/main/java/com/dotmarketing/startup/runonce/Task241009CreatePostgresJobQueueTables.java +++ b/dotCMS/src/main/java/com/dotmarketing/startup/runonce/Task241009CreatePostgresJobQueueTables.java @@ -1,9 +1,14 @@ package com.dotmarketing.startup.runonce; import com.dotmarketing.common.db.DotConnect; +import com.dotmarketing.common.db.DotDatabaseMetaData; +import com.dotmarketing.db.DbConnectionFactory; import com.dotmarketing.exception.DotDataException; import com.dotmarketing.exception.DotRuntimeException; import com.dotmarketing.startup.StartupTask; +import com.dotmarketing.util.Logger; +import java.sql.Connection; +import java.sql.SQLException; /** * Upgrade task to create the necessary tables and indexes for the Postgres Job Queue @@ -26,13 +31,32 @@ public boolean forceRun() { @Override public void executeUpgrade() throws DotDataException, DotRuntimeException { - // Create the job queue tables - createJobQueueTable(); - createJobTable(); - createJobHistoryTable(); + final Connection connection = DbConnectionFactory.getConnection(); + final DotDatabaseMetaData databaseMetaData = new DotDatabaseMetaData(); - // Create the indexes - createIndexes(); + try { + + // job_queue table + if (!databaseMetaData.tableExists(connection, "job_queue")) { + createJobQueueTable(); + createJobQueueTableIndexes(); + } + + // job table + if (!databaseMetaData.tableExists(connection, "job")) { + createJobTable(); + createJobTableIndexes(); + } + + // job_history table + if (!databaseMetaData.tableExists(connection, "job_history")) { + createJobHistoryTable(); + createJobHistoryTableIndexes(); + } + } catch (SQLException e) { + Logger.error("Error creating job queue tables", e); + throw new DotRuntimeException(e.getMessage(), e); + } } /** @@ -55,6 +79,22 @@ private void createJobQueueTable() throws DotDataException { } } + /** + * Creates the necessary indexes for the job_queue table. + * + * @throws DotDataException if an error occurs while creating the indexes. + */ + private void createJobQueueTableIndexes() throws DotDataException { + try { + new DotConnect().executeStatement( + "CREATE INDEX idx_job_queue_status ON job_queue (state)"); + new DotConnect().executeStatement( + "CREATE INDEX idx_job_queue_priority_created_at ON job_queue (priority DESC, created_at ASC)"); + } catch (Exception ex) { + throw new DotDataException(ex.getMessage(), ex); + } + } + /** * Creates the job table. * @@ -82,6 +122,25 @@ private void createJobTable() throws DotDataException { } } + /** + * Creates the necessary indexes for the job table. + * + * @throws DotDataException if an error occurs while creating the indexes. + */ + private void createJobTableIndexes() throws DotDataException { + try { + new DotConnect().executeStatement( + "CREATE INDEX idx_job_parameters ON job USING GIN (parameters)"); + new DotConnect().executeStatement( + "CREATE INDEX idx_job_result ON job USING GIN (result)"); + new DotConnect().executeStatement("CREATE INDEX idx_job_status ON job (state)"); + new DotConnect().executeStatement( + "CREATE INDEX idx_job_created_at ON job (created_at)"); + } catch (Exception ex) { + throw new DotDataException(ex.getMessage(), ex); + } + } + /** * Creates the job_history table. * @@ -105,23 +164,12 @@ private void createJobHistoryTable() throws DotDataException { } /** - * Creates the necessary indexes for the job queue tables. + * Creates the necessary indexes for the job_history table. * * @throws DotDataException if an error occurs while creating the indexes. */ - private void createIndexes() throws DotDataException { + private void createJobHistoryTableIndexes() throws DotDataException { try { - new DotConnect().executeStatement( - "CREATE INDEX idx_job_queue_status ON job_queue (state)"); - new DotConnect().executeStatement( - "CREATE INDEX idx_job_queue_priority_created_at ON job_queue (priority DESC, created_at ASC)"); - new DotConnect().executeStatement( - "CREATE INDEX idx_job_parameters ON job USING GIN (parameters)"); - new DotConnect().executeStatement( - "CREATE INDEX idx_job_result ON job USING GIN (result)"); - new DotConnect().executeStatement("CREATE INDEX idx_job_status ON job (state)"); - new DotConnect().executeStatement( - "CREATE INDEX idx_job_created_at ON job (created_at)"); new DotConnect().executeStatement( "CREATE INDEX idx_job_history_job_id ON job_history (job_id)"); new DotConnect().executeStatement( diff --git a/dotcms-integration/src/test/java/com/dotmarketing/startup/runonce/Task241009CreatePostgresJobQueueTablesTest.java b/dotcms-integration/src/test/java/com/dotmarketing/startup/runonce/Task241009CreatePostgresJobQueueTablesTest.java index 0cf058d4e07..6f68e59edd5 100644 --- a/dotcms-integration/src/test/java/com/dotmarketing/startup/runonce/Task241009CreatePostgresJobQueueTablesTest.java +++ b/dotcms-integration/src/test/java/com/dotmarketing/startup/runonce/Task241009CreatePostgresJobQueueTablesTest.java @@ -11,8 +11,10 @@ import com.dotmarketing.exception.DotDataException; import com.dotmarketing.exception.DotRuntimeException; import com.dotmarketing.exception.DotSecurityException; +import com.dotmarketing.util.Logger; import java.sql.Connection; import java.sql.SQLException; +import org.junit.Assert; import org.junit.BeforeClass; import org.junit.Test; @@ -36,13 +38,12 @@ public static void setup() throws Exception { // Setting web app environment IntegrationTestInitService.getInstance().init(); - LocalTransaction.wrap(Task241009CreatePostgresJobQueueTablesTest::dropTablesIfExist); } /** * Drops the job queue tables if they exist. */ - private static void dropTablesIfExist() { + private void dropTablesIfExist() { try { @@ -79,6 +80,75 @@ private static void dropTablesIfExist() { @Test public void executeTaskUpgrade() throws SQLException, DotDataException, DotSecurityException { + try { + // First, ensure the tables do not exist + LocalTransaction.wrap(() -> { + try { + dropTablesIfExist(); + } catch (Exception e) { + throw new DotRuntimeException(e); + } + }); + + // Running the upgrade task and validating the tables were created + executeUpgradeAndValidate(); + } finally { + DbConnectionFactory.closeSilently(); + } + } + + /** + * Method to test {@link Task241009CreatePostgresJobQueueTables#executeUpgrade()} and + * {@link Task241009CreatePostgresJobQueueTables#forceRun()}. + *

+ * Given Scenario: The job queue tables do not exist, and the upgrade task is run twice. + *

+ * Expected Result: The job queue tables will be created after running the upgrade task the + * first time and should not fail when running the upgrade task again. + * + * @throws SQLException if a SQL error occurs. + * @throws DotDataException if a data access error occurs. + * @throws DotSecurityException if a security error occurs. + */ + @Test + public void executeTaskUpgradeTwice() + throws SQLException, DotDataException, DotSecurityException { + + try { + // First, ensure the tables do not exist + LocalTransaction.wrap(() -> { + try { + dropTablesIfExist(); + } catch (Exception e) { + throw new DotRuntimeException(e); + } + }); + + // Run the upgrade task for the first time, it should create the tables + executeUpgradeAndValidate(); + + // Run the upgrade task again, should not fail + LocalTransaction.wrap(() -> { + try { + final var task = new Task241009CreatePostgresJobQueueTables(); + task.executeUpgrade(); + } catch (Exception e) { + final var message = "The upgrade task should not fail when the tables already exist"; + Logger.error(message, e); + Assert.fail(message); + } + }); + } finally { + DbConnectionFactory.closeSilently(); + } + } + + /** + * Executes the upgrade task and validates the job queue tables were created. + */ + private static void executeUpgradeAndValidate() + throws SQLException, DotDataException, DotSecurityException { + final var task = new Task241009CreatePostgresJobQueueTables(); final Connection connection = DbConnectionFactory.getConnection(); final DotDatabaseMetaData databaseMetaData = new DotDatabaseMetaData(); @@ -102,4 +172,5 @@ public void executeTaskUpgrade() throws SQLException, DotDataException, DotSecur assertTrue(databaseMetaData.tableExists(connection, "job")); assertTrue(databaseMetaData.tableExists(connection, "job_history")); } + } \ No newline at end of file