Skip to content

Commit 22db0d9

Browse files
committed
Introduce MigrationContext to be prepared for JDBC migration refactor
1 parent bce84e0 commit 22db0d9

10 files changed

Lines changed: 164 additions & 62 deletions

File tree

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package io.ebean.migration;
2+
3+
import java.sql.Connection;
4+
5+
/**
6+
* The current context while a migration runs.
7+
* <p>
8+
* This is used to provide meta-informations in JDBC migrations and mainly provides a read-only access
9+
* to a subset of MigrationConfig.
10+
* <p>
11+
* It is possible to provide an extended implementation in <code>MigrationEngine.run(context)</code>,
12+
* which is accessible in JdbcMigration. So you can create a EbeanMigrationContext, so that you can
13+
* access the current ebean server in the JDBC migration.
14+
*
15+
* @author Roland Praml, FOCONIS AG
16+
*/
17+
public interface MigrationContext {
18+
/**
19+
* The current connection. Note: During migration, this connection is always the same.
20+
* You must not close this connection!
21+
*/
22+
Connection connection();
23+
24+
/**
25+
* The migration path of SQL migrations. You can use this, to load additional SQL resources
26+
* in your JDBC migration or determine, if this JDBC migration is for a particular path.
27+
* This can be used if you have multiple ebean servers for different databases.
28+
*/
29+
String migrationPath();
30+
31+
/**
32+
* The platform of the current migration run. (e.g. <code>sqlserver17</code>)
33+
*/
34+
String platform();
35+
36+
/**
37+
* The base platform of the current migration run. (e.g. <code>sqlserver</code>)
38+
*/
39+
String basePlatform();
40+
41+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package io.ebean.migration.runner;
2+
3+
import io.ebean.migration.MigrationConfig;
4+
import io.ebean.migration.MigrationContext;
5+
6+
import java.sql.Connection;
7+
8+
/**
9+
* A default implementation of the MigrationContext.
10+
*
11+
* @author Roland Praml, FOCONIS AG
12+
*/
13+
public class DefaultMigrationContext implements MigrationContext {
14+
private final Connection connection;
15+
private final String migrationPath;
16+
private final String platform;
17+
private final String basePlatform;
18+
19+
public DefaultMigrationContext(MigrationConfig config, Connection connection) {
20+
this.connection = connection;
21+
this.migrationPath = config.getMigrationPath();
22+
this.platform = config.getPlatform();
23+
this.basePlatform = config.getBasePlatform();
24+
}
25+
26+
@Override
27+
public Connection connection() {
28+
return connection;
29+
}
30+
31+
@Override
32+
public String migrationPath() {
33+
return migrationPath;
34+
}
35+
36+
@Override
37+
public String platform() {
38+
return platform;
39+
}
40+
41+
@Override
42+
public String basePlatform() {
43+
return basePlatform;
44+
}
45+
}

ebean-migration/src/main/java/io/ebean/migration/runner/FirstCheck.java

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package io.ebean.migration.runner;
22

33
import io.ebean.migration.MigrationConfig;
4+
import io.ebean.migration.MigrationContext;
45

56
import java.sql.Connection;
67
import java.sql.SQLException;
@@ -15,17 +16,17 @@ final class FirstCheck {
1516

1617
final MigrationConfig config;
1718
final MigrationPlatform platform;
18-
final Connection connection;
19+
final MigrationContext context;
1920
final String schema;
2021
final String table;
2122
final String sqlTable;
2223
boolean tableKnownToExist;
2324
private int count;
2425

25-
FirstCheck(MigrationConfig config, Connection connection, MigrationPlatform platform) {
26+
FirstCheck(MigrationConfig config, MigrationContext context, MigrationPlatform platform) {
2627
this.config = config;
2728
this.platform = platform;
28-
this.connection = connection;
29+
this.context = context;
2930
this.schema = config.getDbSchema();
3031
this.table = config.getMetaTable();
3132
this.sqlTable = schema != null ? schema + '.' + table : table;
@@ -80,7 +81,7 @@ private int checksumFor(LocalMigrationResource local) {
8081
}
8182

8283
List<MigrationMetaRow> fastRead() throws SQLException {
83-
return platform.fastReadMigrations(sqlTable, connection);
84+
return platform.fastReadMigrations(sqlTable, context.connection());
8485
}
8586

8687
int count() {

ebean-migration/src/main/java/io/ebean/migration/runner/MigrationEngine.java

Lines changed: 52 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import io.avaje.applog.AppLog;
44
import io.ebean.migration.MigrationConfig;
5+
import io.ebean.migration.MigrationContext;
56
import io.ebean.migration.MigrationException;
67
import io.ebean.migration.MigrationResource;
78

@@ -35,51 +36,63 @@ public MigrationEngine(MigrationConfig migrationConfig, boolean checkStateOnly)
3536

3637
/**
3738
* Run the migrations if there are any that need running.
39+
*
40+
* @param connection the connection to run on. Note the connection will be closed.
3841
*/
3942
public List<MigrationResource> run(Connection connection) {
4043
try {
41-
long startMs = System.currentTimeMillis();
42-
LocalMigrationResources resources = new LocalMigrationResources(migrationConfig);
43-
if (!resources.readResources() && !resources.readInitResources()) {
44-
log.log(DEBUG, "no migrations to check");
45-
return emptyList();
46-
}
47-
long splitMs = System.currentTimeMillis() - startMs;
48-
final var platform = derivePlatform(migrationConfig, connection);
49-
final var firstCheck = new FirstCheck(migrationConfig, connection, platform);
50-
if (fastMode && firstCheck.fastModeCheck(resources.versions())) {
51-
long checkMs = System.currentTimeMillis() - startMs;
52-
log.log(INFO, "DB migrations completed in {0}ms - totalMigrations:{1} readResources:{2}ms", checkMs, firstCheck.count(), splitMs);
53-
return emptyList();
54-
}
55-
// ensure running with autoCommit false
56-
setAutoCommitFalse(connection);
57-
58-
final MigrationTable table = initialiseMigrationTable(firstCheck, connection);
59-
try {
60-
List<MigrationResource> result = runMigrations(table, resources.versions());
61-
connection.commit();
62-
if (!checkStateOnly) {
63-
long commitMs = System.currentTimeMillis();
64-
log.log(INFO, "DB migrations completed in {0}ms - executed:{1} totalMigrations:{2} mode:{3}", (commitMs - startMs), table.count(), table.size(), table.mode());
65-
int countNonTransactional = table.runNonTransactional();
66-
if (countNonTransactional > 0) {
67-
log.log(INFO, "Non-transactional DB migrations completed in {0}ms - executed:{1}", (System.currentTimeMillis() - commitMs), countNonTransactional);
68-
}
44+
return run(new DefaultMigrationContext(migrationConfig, connection));
45+
} finally {
46+
close(connection);
47+
}
48+
}
49+
50+
/**
51+
* Run the migrations if there are any that need running. (Does not close connection)
52+
*/
53+
public List<MigrationResource> run(MigrationContext context) {
54+
55+
long startMs = System.currentTimeMillis();
56+
LocalMigrationResources resources = new LocalMigrationResources(migrationConfig);
57+
if (!resources.readResources() && !resources.readInitResources()) {
58+
log.log(DEBUG, "no migrations to check");
59+
return emptyList();
60+
}
61+
62+
var connection = context.connection();
63+
long splitMs = System.currentTimeMillis() - startMs;
64+
final var platform = derivePlatform(migrationConfig, connection);
65+
final var firstCheck = new FirstCheck(migrationConfig, context, platform);
66+
if (fastMode && firstCheck.fastModeCheck(resources.versions())) {
67+
long checkMs = System.currentTimeMillis() - startMs;
68+
log.log(INFO, "DB migrations completed in {0}ms - totalMigrations:{1} readResources:{2}ms", checkMs, firstCheck.count(), splitMs);
69+
return emptyList();
70+
}
71+
// ensure running with autoCommit false
72+
setAutoCommitFalse(connection);
73+
74+
final MigrationTable table = initialiseMigrationTable(firstCheck, connection);
75+
try {
76+
List<MigrationResource> result = runMigrations(table, resources.versions());
77+
connection.commit();
78+
if (!checkStateOnly) {
79+
long commitMs = System.currentTimeMillis();
80+
log.log(INFO, "DB migrations completed in {0}ms - executed:{1} totalMigrations:{2} mode:{3}", (commitMs - startMs), table.count(), table.size(), table.mode());
81+
int countNonTransactional = table.runNonTransactional();
82+
if (countNonTransactional > 0) {
83+
log.log(INFO, "Non-transactional DB migrations completed in {0}ms - executed:{1}", (System.currentTimeMillis() - commitMs), countNonTransactional);
6984
}
70-
return result;
71-
} catch (MigrationException e) {
72-
rollback(connection);
73-
throw e;
74-
} catch (Throwable e) {
75-
log.log(ERROR, "Perform rollback due to DB migration error", e);
76-
rollback(connection);
77-
throw new MigrationException("Error running DB migrations", e);
78-
} finally {
79-
table.unlockMigrationTable();
8085
}
86+
return result;
87+
} catch (MigrationException e) {
88+
rollback(connection);
89+
throw e;
90+
} catch (Throwable e) {
91+
log.log(ERROR, "Perform rollback due to DB migration error", e);
92+
rollback(connection);
93+
throw new MigrationException("Error running DB migrations", e);
8194
} finally {
82-
close(connection);
95+
table.unlockMigrationTable();
8396
}
8497
}
8598

ebean-migration/src/main/java/io/ebean/migration/runner/MigrationTable.java

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ final class MigrationTable {
2626
private static final int AUTO_PATCH_CHECKSUM = -1;
2727

2828
private final MigrationConfig config;
29-
private final Connection connection;
29+
private final MigrationContext context;
3030
private final boolean checkStateOnly;
3131
private boolean earlyChecksumMode;
3232
private final MigrationPlatform platform;
@@ -72,18 +72,18 @@ final class MigrationTable {
7272
private int executionCount;
7373
private boolean patchLegacyChecksums;
7474
private MigrationMetaRow initMetaRow;
75-
private boolean tableKnownToExist;
75+
private final boolean tableKnownToExist;
7676

7777
public MigrationTable(FirstCheck firstCheck, boolean checkStateOnly) {
7878
this.config = firstCheck.config;
7979
this.platform = firstCheck.platform;
80-
this.connection = firstCheck.connection;
80+
this.context = firstCheck.context;
8181
this.schema = firstCheck.schema;
8282
this.table = firstCheck.table;
8383
this.sqlTable = firstCheck.sqlTable;
8484
this.tableKnownToExist = firstCheck.tableKnownToExist;
8585

86-
this.scriptRunner = new MigrationScriptRunner(connection, platform);
86+
this.scriptRunner = new MigrationScriptRunner(context.connection(), platform);
8787
this.checkStateOnly = checkStateOnly;
8888
this.earlyChecksumMode = config.isEarlyChecksumMode();
8989
this.migrations = new LinkedHashMap<>();
@@ -142,7 +142,7 @@ private ScriptTransform createScriptTransform(MigrationConfig config) {
142142
void createIfNeededAndLock() throws SQLException, IOException {
143143
SQLException suppressedException = null;
144144
if (!tableKnownToExist) {
145-
MigrationSchema.createIfNeeded(config, connection);
145+
MigrationSchema.createIfNeeded(config, context.connection());
146146
if (!tableExists()) {
147147
try {
148148
createTable();
@@ -174,14 +174,14 @@ void createIfNeededAndLock() throws SQLException, IOException {
174174
* contain all the executed migrations in that case.
175175
*/
176176
private void obtainLockWithWait() throws SQLException {
177-
platform.lockMigrationTable(sqlTable, connection);
177+
platform.lockMigrationTable(sqlTable, context.connection());
178178
}
179179

180180
/**
181181
* Release a lock on the migration table (MySql, MariaDB only).
182182
*/
183183
void unlockMigrationTable() {
184-
platform.unlockMigrationTable(sqlTable, connection);
184+
platform.unlockMigrationTable(sqlTable, context.connection());
185185
}
186186

187187
/**
@@ -191,12 +191,13 @@ void unlockMigrationTable() {
191191
* executed during the wait for the lock.
192192
*/
193193
private void readExistingMigrations() throws SQLException {
194-
for (MigrationMetaRow metaRow : platform.readExistingMigrations(sqlTable, connection)) {
194+
for (MigrationMetaRow metaRow : platform.readExistingMigrations(sqlTable, context.connection())) {
195195
addMigration(metaRow.version(), metaRow);
196196
}
197197
}
198198

199199
void createTable() throws IOException, SQLException {
200+
Connection connection = context.connection();
200201
try {
201202
scriptRunner.runScript(createTableDdl(), "create migration table");
202203
createInitMetaRow().executeInsert(connection, insertSql);
@@ -253,6 +254,7 @@ private ClassLoader classLoader() {
253254
* Return true if the table exists.
254255
*/
255256
boolean tableExists() throws SQLException {
257+
Connection connection = context.connection();
256258
String migTable = table;
257259
DatabaseMetaData metaData = connection.getMetaData();
258260
if (metaData.storesUpperCaseIdentifiers()) {
@@ -351,7 +353,7 @@ boolean skipMigration(int checksum, int checksum2, LocalMigrationResource local,
351353
} else if (patchLegacyChecksums && (existing.checksum() == checksum2 || checksum2 == AUTO_PATCH_CHECKSUM)) {
352354
if (!checkStateOnly) {
353355
log.log(INFO, "Auto patch migration, set early mode checksum on {0} to {1,number} from {2,number}", local.location(), checksum, existing.checksum());
354-
existing.resetChecksum(checksum, connection, updateChecksumSql);
356+
existing.resetChecksum(checksum, context.connection(), updateChecksumSql);
355357
}
356358
return true;
357359

@@ -373,7 +375,7 @@ boolean skipMigration(int checksum, int checksum2, LocalMigrationResource local,
373375
private boolean patchResetChecksum(MigrationMetaRow existing, int newChecksum) throws SQLException {
374376
if (isResetOnVersion(existing.version())) {
375377
if (!checkStateOnly) {
376-
existing.resetChecksum(newChecksum, connection, updateChecksumSql);
378+
existing.resetChecksum(newChecksum, context.connection(), updateChecksumSql);
377379
}
378380
return true;
379381
} else {
@@ -405,7 +407,7 @@ private void executeMigration(LocalMigrationResource local, String script, int c
405407
}
406408
if (existing != null) {
407409
existing.rerun(checksum, exeMillis, envUserName, runOn);
408-
existing.executeUpdate(connection, updateSql);
410+
existing.executeUpdate(context.connection(), updateSql);
409411
} else {
410412
insertIntoHistory(local, checksum, exeMillis);
411413
}
@@ -424,7 +426,7 @@ private long executeMigration(LocalMigrationResource local, String script) throw
424426
if (local instanceof LocalJdbcMigrationResource) {
425427
JdbcMigration migration = ((LocalJdbcMigrationResource) local).migration();
426428
log.log(INFO, "Executing jdbc migration version: {0} - {1}", local.version(), migration);
427-
migration.migrate(connection);
429+
migration.migrate(context.connection());
428430
} else {
429431
log.log(DEBUG, "run migration {0}", local.location());
430432
scriptRunner.runScript(script, "run migration version: " + local.version());
@@ -435,7 +437,7 @@ private long executeMigration(LocalMigrationResource local, String script) throw
435437

436438
private void insertIntoHistory(LocalMigrationResource local, int checksum, long exeMillis) throws SQLException {
437439
MigrationMetaRow metaRow = createMetaRow(local, checksum, exeMillis);
438-
metaRow.executeInsert(connection, insertSql);
440+
metaRow.executeInsert(context.connection(), insertSql);
439441
addMigration(local.key(), metaRow);
440442
}
441443

@@ -525,7 +527,7 @@ List<MigrationResource> runAll(List<LocalMigrationResource> localVersions) throw
525527
}
526528
if (patchLegacyChecksums && !checkStateOnly) {
527529
// only patch the legacy checksums once
528-
initMetaRow.resetChecksum(EARLY_MODE_CHECKSUM, connection, updateChecksumSql);
530+
initMetaRow.resetChecksum(EARLY_MODE_CHECKSUM, context.connection(), updateChecksumSql);
529531
}
530532
return checkMigrations;
531533
}

ebean-migration/src/test/java/io/ebean/migration/runner/MigrationTable1Test.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ public void shutdown() {
4444

4545

4646
private MigrationTable migrationTable(Connection conn) {
47-
var fc = new FirstCheck(config, conn, platform);
47+
var fc = new FirstCheck(config, new DefaultMigrationContext(config, conn), platform);
4848
return new MigrationTable(fc, false);
4949
}
5050

ebean-migration/src/test/java/io/ebean/migration/runner/MigrationTable2Test.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ public class MigrationTable2Test {
1212

1313

1414
private static MigrationTable migrationTable(MigrationConfig config) {
15-
var fc = new FirstCheck(config, null, new MigrationPlatform());
15+
var fc = new FirstCheck(config, new DefaultMigrationContext(config, null), new MigrationPlatform());
1616
return new MigrationTable(fc, false);
1717
}
1818

ebean-migration/src/test/java/io/ebean/migration/runner/MigrationTableAsyncTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,7 @@ private void runTest(boolean withExisting) throws SQLException, InterruptedExcep
204204
}
205205

206206
private static MigrationTable migrationTable(MigrationPlatform platform, Connection connection) {
207-
var fc = new FirstCheck(config, connection, platform);
207+
var fc = new FirstCheck(config, new DefaultMigrationContext(config, connection), platform);
208208
return new MigrationTable(fc, false);
209209
}
210210

0 commit comments

Comments
 (0)