Skip to content

Commit 2bc346d

Browse files
committed
diskquota/jdbc: poll on FK migration race recovery
Concurrent migrateForeignKeys callers could fail OracleForeignKey MigrationIT.migrateIsConcurrentStartupSafe sporadically. Oracle's ALTER TABLE uses NOWAIT, and a peer thread that gets ORA-00054 on DROP CONSTRAINT could re-check the FK state while the peer was still between its drop and its add, observe an in-between state, and rethrow. on-behalf-of: @camptocamp <info@camptocamp.com>
1 parent 39049e4 commit 2bc346d

1 file changed

Lines changed: 26 additions & 1 deletion

File tree

  • geowebcache/diskquota/jdbc/src/main/java/org/geowebcache/diskquota/jdbc

geowebcache/diskquota/jdbc/src/main/java/org/geowebcache/diskquota/jdbc/SQLDialect.java

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,7 @@ private Void upgradeTilepageForeignKey(DatabaseMetaData dbmd, String schema, Sim
184184
jdbcOperations.execute(drop);
185185
jdbcOperations.execute(add);
186186
} catch (DataAccessException raceLikely) {
187-
if (isTilepageFkAlreadyMigrated(dbmd, schema, tilepageName)) {
187+
if (awaitConcurrentMigration(dbmd, schema, tilepageName)) {
188188
LOG.fine(() -> "TILEPAGE FK was migrated concurrently by another instance "
189189
+ "while this instance was trying to drop %s; accepting concurrent migration"
190190
.formatted(fkName));
@@ -212,6 +212,31 @@ protected String tilepageFkAddSql(String prefixedTilepageName, String prefix) {
212212
.formatted(prefixedTilepageName, prefix);
213213
}
214214

215+
private static final int CONCURRENT_MIGRATION_RECHECK_ATTEMPTS = 20;
216+
private static final long CONCURRENT_MIGRATION_RECHECK_BACKOFF_MS = 100L;
217+
218+
/**
219+
* Polls {@link #isTilepageFkAlreadyMigrated} after a failed drop/add to absorb the window in which a peer instance
220+
* is mid-migration. Oracle's {@code ALTER TABLE} uses NOWAIT, so a concurrent peer can fail us with ORA-00054 while
221+
* it still has the drop committed but not yet the add; a one-shot recheck would observe the in-flight state and
222+
* give up.
223+
*/
224+
private boolean awaitConcurrentMigration(DatabaseMetaData dbmd, String schema, String tilepageName)
225+
throws SQLException {
226+
for (int attempt = 0; attempt < CONCURRENT_MIGRATION_RECHECK_ATTEMPTS; attempt++) {
227+
if (isTilepageFkAlreadyMigrated(dbmd, schema, tilepageName)) {
228+
return true;
229+
}
230+
try {
231+
Thread.sleep(CONCURRENT_MIGRATION_RECHECK_BACKOFF_MS);
232+
} catch (InterruptedException e) {
233+
Thread.currentThread().interrupt();
234+
return false;
235+
}
236+
}
237+
return false;
238+
}
239+
215240
/** Re-checks FK state after a failed migration attempt to detect a concurrent migration from another instance. */
216241
private boolean isTilepageFkAlreadyMigrated(DatabaseMetaData dbmd, String schema, String tilepageName)
217242
throws SQLException {

0 commit comments

Comments
 (0)