Skip to content

Commit 6055840

Browse files
authored
Merge pull request #148 from ebean-orm/feature/refactor-fastCheck
Refactor fastCheck, move startMs and move setAutoCommitFalse()
2 parents c89476e + 7877351 commit 6055840

8 files changed

Lines changed: 148 additions & 103 deletions

File tree

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
package io.ebean.migration.runner;
2+
3+
import io.ebean.migration.MigrationConfig;
4+
5+
import java.sql.Connection;
6+
import java.sql.SQLException;
7+
import java.util.List;
8+
import java.util.Map;
9+
import java.util.stream.Collectors;
10+
11+
/**
12+
* First initial check to see if migrations exist and exactly match.
13+
*/
14+
final class FirstCheck {
15+
16+
final MigrationConfig config;
17+
final MigrationPlatform platform;
18+
final Connection connection;
19+
final String schema;
20+
final String table;
21+
final String sqlTable;
22+
boolean tableKnownToExist;
23+
private int count;
24+
25+
FirstCheck(MigrationConfig config, Connection connection, MigrationPlatform platform) {
26+
this.config = config;
27+
this.platform = platform;
28+
this.connection = connection;
29+
this.schema = config.getDbSchema();
30+
this.table = config.getMetaTable();
31+
this.sqlTable = schema != null ? schema + '.' + table : table;
32+
}
33+
34+
MigrationTable initTable(boolean checkStateOnly) {
35+
return new MigrationTable(this, checkStateOnly);
36+
}
37+
38+
boolean fastModeCheck(List<LocalMigrationResource> versions) {
39+
try {
40+
final List<MigrationMetaRow> rows = fastRead();
41+
tableKnownToExist = !rows.isEmpty();
42+
if (rows.size() != versions.size() + 1) {
43+
// difference in count of migrations
44+
return false;
45+
}
46+
final Map<String, Integer> dbChecksums = dbChecksumMap(rows);
47+
for (LocalMigrationResource local : versions) {
48+
Integer dbChecksum = dbChecksums.get(local.key());
49+
if (dbChecksum == null) {
50+
// no match, unexpected missing migration
51+
return false;
52+
}
53+
int localChecksum = checksumFor(local);
54+
if (localChecksum != dbChecksum) {
55+
// no match, perhaps repeatable migration change
56+
return false;
57+
}
58+
}
59+
// successful fast check
60+
count = versions.size();
61+
return true;
62+
} catch (SQLException e) {
63+
// probably migration table does not exist
64+
return false;
65+
}
66+
}
67+
68+
private static Map<String, Integer> dbChecksumMap(List<MigrationMetaRow> rows) {
69+
return rows.stream().collect(Collectors.toMap(MigrationMetaRow::version, MigrationMetaRow::checksum));
70+
}
71+
72+
private int checksumFor(LocalMigrationResource local) {
73+
if (local instanceof LocalUriMigrationResource) {
74+
return ((LocalUriMigrationResource) local).checksum();
75+
} else if (local instanceof LocalDdlMigrationResource) {
76+
return Checksum.calculate(local.content());
77+
} else {
78+
return ((LocalJdbcMigrationResource) local).checksum();
79+
}
80+
}
81+
82+
List<MigrationMetaRow> fastRead() throws SQLException {
83+
return platform.fastReadMigrations(sqlTable, connection);
84+
}
85+
86+
int count() {
87+
return count;
88+
}
89+
}

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

Lines changed: 17 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,11 @@
77

88
import java.sql.Connection;
99
import java.sql.SQLException;
10-
import java.util.Collections;
1110
import java.util.List;
12-
import java.util.Map;
13-
import java.util.stream.Collectors;
1411

1512
import static java.lang.System.Logger.Level.*;
1613
import static java.lang.System.Logger.Level.WARNING;
14+
import static java.util.Collections.emptyList;
1715

1816
/**
1917
* Actually runs the migrations.
@@ -25,8 +23,6 @@ public class MigrationEngine {
2523
private final MigrationConfig migrationConfig;
2624
private final boolean checkStateOnly;
2725
private final boolean fastMode;
28-
private int fastModeCount;
29-
private MigrationTable table;
3026

3127
/**
3228
* Create with the MigrationConfig.
@@ -42,23 +38,25 @@ public MigrationEngine(MigrationConfig migrationConfig, boolean checkStateOnly)
4238
*/
4339
public List<MigrationResource> run(Connection connection) {
4440
try {
41+
long startMs = System.currentTimeMillis();
4542
LocalMigrationResources resources = new LocalMigrationResources(migrationConfig);
4643
if (!resources.readResources() && !resources.readInitResources()) {
4744
log.log(DEBUG, "no migrations to check");
48-
return Collections.emptyList();
45+
return emptyList();
4946
}
50-
long startMs = System.currentTimeMillis();
51-
setAutoCommitFalse(connection);
52-
table = initMigrationTable(connection);
53-
if (fastMode && fastModeCheck(resources.versions())) {
47+
final var platform = derivePlatform(migrationConfig, connection);
48+
final var firstCheck = new FirstCheck(migrationConfig, connection, platform);
49+
if (fastMode && firstCheck.fastModeCheck(resources.versions())) {
5450
long checkMs = System.currentTimeMillis() - startMs;
55-
log.log(INFO, "DB migrations completed in {0}ms - totalMigrations:{1}", checkMs, fastModeCount);
56-
return Collections.emptyList();
51+
log.log(INFO, "DB migrations completed in {0}ms - totalMigrations:{1}", checkMs, firstCheck.count());
52+
return emptyList();
5753
}
54+
// ensure running with autoCommit false
55+
setAutoCommitFalse(connection);
5856

59-
initialiseMigrationTable(connection);
57+
final MigrationTable table = initialiseMigrationTable(firstCheck, connection);
6058
try {
61-
List<MigrationResource> result = runMigrations(resources.versions());
59+
List<MigrationResource> result = runMigrations(table, resources.versions());
6260
connection.commit();
6361
if (!checkStateOnly) {
6462
long commitMs = System.currentTimeMillis();
@@ -92,57 +90,11 @@ private static void setAutoCommitFalse(Connection connection) {
9290
}
9391
}
9492

95-
private MigrationTable initMigrationTable(Connection connection) {
96-
final MigrationPlatform platform = derivePlatformName(migrationConfig, connection);
97-
return new MigrationTable(migrationConfig, connection, checkStateOnly, platform);
98-
}
99-
100-
private boolean fastModeCheck(List<LocalMigrationResource> versions) {
101-
try {
102-
final List<MigrationMetaRow> rows = table.fastRead();
103-
if (rows.size() != versions.size() + 1) {
104-
// difference in count of migrations
105-
return false;
106-
}
107-
final Map<String, Integer> dbChecksums = dbChecksumMap(rows);
108-
for (LocalMigrationResource local : versions) {
109-
Integer dbChecksum = dbChecksums.get(local.key());
110-
if (dbChecksum == null) {
111-
// no match, unexpected missing migration
112-
return false;
113-
}
114-
int localChecksum = checksumFor(local);
115-
if (localChecksum != dbChecksum) {
116-
// no match, perhaps repeatable migration change
117-
return false;
118-
}
119-
}
120-
// successful fast check
121-
fastModeCount = versions.size();
122-
return true;
123-
} catch (SQLException e) {
124-
// probably migration table does not exist
125-
return false;
126-
}
127-
}
128-
129-
private static Map<String, Integer> dbChecksumMap(List<MigrationMetaRow> rows) {
130-
return rows.stream().collect(Collectors.toMap(MigrationMetaRow::version, MigrationMetaRow::checksum));
131-
}
132-
133-
private int checksumFor(LocalMigrationResource local) {
134-
if (local instanceof LocalUriMigrationResource) {
135-
return ((LocalUriMigrationResource)local).checksum();
136-
} else if (local instanceof LocalDdlMigrationResource) {
137-
return Checksum.calculate(local.content());
138-
} else {
139-
return ((LocalJdbcMigrationResource) local).checksum();
140-
}
141-
}
142-
143-
private void initialiseMigrationTable(Connection connection) {
93+
private MigrationTable initialiseMigrationTable(FirstCheck firstCheck, Connection connection) {
14494
try {
95+
final MigrationTable table = firstCheck.initTable(checkStateOnly);
14596
table.createIfNeededAndLock();
97+
return table;
14698
} catch (Throwable e) {
14799
rollback(connection);
148100
throw new MigrationException("Error initialising db migrations table", e);
@@ -152,7 +104,7 @@ private void initialiseMigrationTable(Connection connection) {
152104
/**
153105
* Run all the migrations as needed.
154106
*/
155-
private List<MigrationResource> runMigrations(List<LocalMigrationResource> localVersions) throws SQLException {
107+
private List<MigrationResource> runMigrations(MigrationTable table, List<LocalMigrationResource> localVersions) throws SQLException {
156108
// get the migrations in version order
157109
if (table.isEmpty()) {
158110
LocalMigrationResource initVersion = lastInitVersion();
@@ -182,7 +134,7 @@ private LocalMigrationResource lastInitVersion() {
182134
/**
183135
* Return the platform deriving from connection if required.
184136
*/
185-
private MigrationPlatform derivePlatformName(MigrationConfig migrationConfig, Connection connection) {
137+
private MigrationPlatform derivePlatform(MigrationConfig migrationConfig, Connection connection) {
186138
final String platform = migrationConfig.getPlatform();
187139
if (platform != null) {
188140
return DbNameUtil.platform(platform);

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

Lines changed: 9 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -74,13 +74,15 @@ final class MigrationTable {
7474
private MigrationMetaRow initMetaRow;
7575
private boolean tableKnownToExist;
7676

77-
/**
78-
* Construct with server, configuration and jdbc connection (DB admin user).
79-
*/
80-
MigrationTable(MigrationConfig config, Connection connection, boolean checkStateOnly, MigrationPlatform platform) {
81-
this.config = config;
82-
this.platform = platform;
83-
this.connection = connection;
77+
public MigrationTable(FirstCheck firstCheck, boolean checkStateOnly) {
78+
this.config = firstCheck.config;
79+
this.platform = firstCheck.platform;
80+
this.connection = firstCheck.connection;
81+
this.schema = firstCheck.schema;
82+
this.table = firstCheck.table;
83+
this.sqlTable = firstCheck.sqlTable;
84+
this.tableKnownToExist = firstCheck.tableKnownToExist;
85+
8486
this.scriptRunner = new MigrationScriptRunner(connection, platform);
8587
this.checkStateOnly = checkStateOnly;
8688
this.earlyChecksumMode = config.isEarlyChecksumMode();
@@ -93,11 +95,8 @@ final class MigrationTable {
9395
this.minVersionFailMessage = config.getMinVersionFailMessage();
9496
this.skipMigrationRun = config.isSkipMigrationRun();
9597
this.skipChecksum = config.isSkipChecksum();
96-
this.schema = config.getDbSchema();
97-
this.table = config.getMetaTable();
9898
this.basePlatformName = config.getBasePlatform();
9999
this.platformName = config.getPlatform();
100-
this.sqlTable = initSqlTable();
101100
this.insertSql = MigrationMetaRow.insertSql(sqlTable);
102101
this.updateSql = MigrationMetaRow.updateSql(sqlTable);
103102
this.updateChecksumSql = MigrationMetaRow.updateChecksumSql(sqlTable);
@@ -109,14 +108,6 @@ private MigrationVersion initMinVersion(String minVersion) {
109108
return (minVersion == null || minVersion.isEmpty()) ? null : MigrationVersion.parse(minVersion);
110109
}
111110

112-
private String initSqlTable() {
113-
if (schema != null) {
114-
return schema + "." + table;
115-
} else {
116-
return table;
117-
}
118-
}
119-
120111
private String sqlPrimaryKey() {
121112
return "pk_" + table;
122113
}
@@ -193,13 +184,6 @@ void unlockMigrationTable() {
193184
platform.unlockMigrationTable(sqlTable, connection);
194185
}
195186

196-
List<MigrationMetaRow> fastRead() throws SQLException {
197-
final var result = platform.fastReadMigrations(sqlTable, connection);
198-
// if we know the migration table exists we can skip those checks
199-
tableKnownToExist = !result.isEmpty();
200-
return result;
201-
}
202-
203187
/**
204188
* Read the migration table with details on what migrations have run.
205189
* This must execute after we have completed the wait for the lock on

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

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,12 @@ public void shutdown() {
4242
dataSource.shutdown();
4343
}
4444

45+
46+
private MigrationTable migrationTable(Connection conn) {
47+
var fc = new FirstCheck(config, conn, platform);
48+
return new MigrationTable(fc, false);
49+
}
50+
4551
@Test
4652
public void testMigrationTableBase() throws Exception {
4753

@@ -50,7 +56,7 @@ public void testMigrationTableBase() throws Exception {
5056
MigrationRunner runner = new MigrationRunner(config);
5157
runner.run(dataSource);
5258
try (Connection conn = dataSource.getConnection()) {
53-
MigrationTable table = new MigrationTable(config, conn, false, platform);
59+
MigrationTable table = migrationTable(conn);
5460
table.createIfNeededAndLock();
5561
assertThat(table.versions()).containsExactly("hello", "1.1", "1.2", "1.2.1", "m2_view");
5662

@@ -68,6 +74,7 @@ public void testMigrationTableBase() throws Exception {
6874
}
6975
}
7076

77+
7178
@Test
7279
public void testMigrationTableRepeatableOk() throws Exception {
7380

@@ -77,7 +84,7 @@ public void testMigrationTableRepeatableOk() throws Exception {
7784
runner.run(dataSource);
7885

7986
try (Connection conn = dataSource.getConnection()) {
80-
MigrationTable table = new MigrationTable(config, conn, false, platform);
87+
MigrationTable table = migrationTable(conn);
8188
table.createIfNeededAndLock();
8289
assertThat(table.versions()).containsExactly("1.1");
8390
table.unlockMigrationTable();
@@ -90,7 +97,7 @@ public void testMigrationTableRepeatableOk() throws Exception {
9097
runner.run(dataSource);
9198

9299
try (Connection conn = dataSource.getConnection()) {
93-
MigrationTable table = new MigrationTable(config, conn, false, platform);
100+
MigrationTable table = migrationTable(conn);
94101
table.createIfNeededAndLock();
95102
assertThat(table.versions()).containsExactly("1.1", "1.2", "m2_view");
96103
table.unlockMigrationTable();
@@ -108,7 +115,7 @@ public void testMigrationTableRepeatableFail() throws Exception {
108115
runner.run(dataSource);
109116

110117
try (Connection conn = dataSource.getConnection()) {
111-
MigrationTable table = new MigrationTable(config, conn, false, platform);
118+
MigrationTable table = migrationTable(conn);
112119
table.createIfNeededAndLock();
113120
assertThat(table.versions()).containsExactly("1.1");
114121
table.unlockMigrationTable();
@@ -141,7 +148,7 @@ public void testMigrationTableRepeatableFail() throws Exception {
141148
assertThat(m3Content).contains("text with ; sign");
142149

143150
// we expect, that 1.1 and 1.2 is executed (but not the R__ script)
144-
MigrationTable table = new MigrationTable(config, conn, false, platform);
151+
MigrationTable table = migrationTable(conn);
145152
table.createIfNeededAndLock();
146153
assertThat(table.versions()).containsExactly("1.1", "1.2");
147154
conn.rollback();

0 commit comments

Comments
 (0)