Skip to content

Commit ec05100

Browse files
wezellclaude
andcommitted
fix(identifier): forceRun() resumes backfill after server restart mid-migration
Previously forceRun() returned false once idx_identifier_base_type existed, permanently abandoning millions of NULL base_type rows if the server restarted mid-backfill. Fix: also return true when hasPendingRows() detects identifier rows with null base_type and non-null asset_subtype. DDL uses IF NOT EXISTS so re-running executeUpgrade() is always safe. Tests: replace incorrect false-when-index-exists assertion with two cases: - returns false only when index exists AND no pending rows remain - returns true when index exists but backfill is incomplete (restart scenario) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent b27bcc9 commit ec05100

2 files changed

Lines changed: 44 additions & 8 deletions

File tree

dotCMS/src/main/java/com/dotmarketing/startup/runonce/Task260331AddBaseTypeColumnToIdentifier.java

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,16 +25,20 @@ public class Task260331AddBaseTypeColumnToIdentifier implements StartupTask {
2525

2626
@Override
2727
public boolean forceRun() {
28-
29-
// only run if the index has not been built yet
3028
try {
29+
// Run if the index has not been created yet
3130
final List<Map<String, Object>> result = new DotConnect()
3231
.setSQL("SELECT 1 FROM pg_indexes WHERE tablename = 'identifier' AND indexname = ?")
3332
.addParam(INDEX_NAME)
3433
.loadObjectResults();
35-
return result.isEmpty();
34+
if (result.isEmpty()) {
35+
return true;
36+
}
37+
// Also run if the backfill is incomplete — handles server restarts mid-migration.
38+
// DDL uses IF NOT EXISTS so re-running executeUpgrade() is always safe.
39+
return PopulateIdentifierBaseTypeJob.hasPendingRows();
3640
} catch (final DotDataException e) {
37-
Logger.error(this, "Error checking for index " + INDEX_NAME + ": " + e.getMessage(), e);
41+
Logger.error(this, "Error in forceRun() for " + INDEX_NAME + ": " + e.getMessage(), e);
3842
return false;
3943
}
4044
}

dotcms-integration/src/test/java/com/dotmarketing/startup/runonce/Task260331AddBaseTypeColumnToIdentifierTest.java

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -63,17 +63,49 @@ public void test_executeUpgrade_addsColumnAndIndex() throws SQLException, DotDat
6363

6464
/**
6565
* Method to test: {@link Task260331AddBaseTypeColumnToIdentifier#forceRun()}
66-
* When: Index already exists
67-
* Should: Return false (task should not re-run)
66+
* When: Index exists AND all rows are already populated (migration complete)
67+
* Should: Return false — no work to do
6868
*/
6969
@Test
70-
public void test_forceRun_returnsFalseWhenIndexExists() throws DotDataException, SQLException {
70+
public void test_forceRun_returnsFalseWhenMigrationComplete() throws DotDataException, SQLException {
7171
cleanUp();
7272

7373
final var task = new Task260331AddBaseTypeColumnToIdentifier();
7474
task.executeUpgrade();
7575

76-
assertFalse("forceRun() should return false when index already exists", task.forceRun());
76+
// Ensure no pending rows remain
77+
new DotConnect().executeStatement(
78+
"UPDATE identifier SET base_type = 1 WHERE base_type IS NULL AND asset_subtype IS NOT NULL");
79+
80+
try {
81+
assertFalse("forceRun() should return false when index exists and no pending rows remain",
82+
task.forceRun());
83+
} finally {
84+
new DotConnect().executeStatement(
85+
"UPDATE identifier SET base_type = NULL WHERE base_type = 1 AND asset_subtype IS NOT NULL");
86+
}
87+
}
88+
89+
/**
90+
* Method to test: {@link Task260331AddBaseTypeColumnToIdentifier#forceRun()}
91+
* When: Index exists BUT rows still have null base_type (server restarted mid-backfill)
92+
* Should: Return true so the backfill job is re-scheduled
93+
*/
94+
@Test
95+
public void test_forceRun_returnsTrueWhenBackfillIncomplete() throws DotDataException, SQLException {
96+
cleanUp();
97+
98+
final var task = new Task260331AddBaseTypeColumnToIdentifier();
99+
task.executeUpgrade();
100+
101+
// Simulate mid-backfill restart: index exists but some rows are still NULL
102+
new DotConnect().executeStatement(
103+
"UPDATE identifier SET base_type = NULL " +
104+
"WHERE asset_subtype IS NOT NULL AND id = (" +
105+
" SELECT id FROM identifier WHERE asset_subtype IS NOT NULL LIMIT 1)");
106+
107+
assertTrue("forceRun() should return true when index exists but backfill is incomplete",
108+
task.forceRun());
77109
}
78110

79111
/**

0 commit comments

Comments
 (0)