Skip to content

Commit 2407569

Browse files
authored
Merge pull request #139 from ebean-orm/feature/137-logical-lock
Fix such that there is a rollback() before LogicalLock unlock()
2 parents 2a1e5eb + 7eac694 commit 2407569

File tree

7 files changed

+74
-27
lines changed

7 files changed

+74
-27
lines changed

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

Lines changed: 20 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
import io.ebean.migration.MigrationException;
66
import io.ebean.migration.MigrationResource;
77

8-
import java.io.IOException;
98
import java.sql.Connection;
109
import java.sql.SQLException;
1110
import java.util.Collections;
@@ -44,7 +43,6 @@ public List<MigrationResource> run(Connection connection) {
4443
}
4544

4645
long startMs = System.currentTimeMillis();
47-
connection.setAutoCommit(false);
4846
MigrationTable table = initialiseMigrationTable(connection);
4947
try {
5048
List<MigrationResource> result = runMigrations(resources.versions(), table, checkStateOnly);
@@ -58,30 +56,34 @@ public List<MigrationResource> run(Connection connection) {
5856
}
5957
}
6058
return result;
59+
} catch (MigrationException e) {
60+
rollback(connection);
61+
throw e;
62+
} catch (Exception e) {
63+
log.log(ERROR, "Perform rollback due to DB migration error", e);
64+
rollback(connection);
65+
throw new MigrationException("Error running DB migrations", e);
6166
} finally {
6267
table.unlockMigrationTable();
6368
}
64-
65-
} catch (MigrationException e) {
66-
rollback(connection);
67-
throw e;
68-
69-
} catch (Exception e) {
70-
rollback(connection);
71-
throw new MigrationException("Error running DB migrations", e);
72-
7369
} finally {
7470
close(connection);
7571
}
7672
}
7773

78-
private MigrationTable initialiseMigrationTable(Connection connection) throws SQLException, IOException {
79-
MigrationPlatform platform = derivePlatformName(migrationConfig, connection);
80-
new MigrationSchema(migrationConfig, connection).createAndSetIfNeeded();
74+
private MigrationTable initialiseMigrationTable(Connection connection) {
75+
try {
76+
connection.setAutoCommit(false);
77+
MigrationPlatform platform = derivePlatformName(migrationConfig, connection);
78+
new MigrationSchema(migrationConfig, connection).createAndSetIfNeeded();
8179

82-
final MigrationTable table = new MigrationTable(migrationConfig, connection, checkStateOnly, platform);
83-
table.createIfNeededAndLock();
84-
return table;
80+
final MigrationTable table = new MigrationTable(migrationConfig, connection, checkStateOnly, platform);
81+
table.createIfNeededAndLock();
82+
return table;
83+
} catch (Exception e) {
84+
rollback(connection);
85+
throw new MigrationException("Error initialising db migrations table", e);
86+
}
8587
}
8688

8789
/**
@@ -144,7 +146,7 @@ private void close(Connection connection) {
144146
/**
145147
* Rollback the connection logging if an error occurs.
146148
*/
147-
private void rollback(Connection connection) {
149+
static void rollback(Connection connection) {
148150
try {
149151
if (connection != null) {
150152
connection.rollback();

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

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

33
import io.ebean.ddlrunner.DdlDetect;
4+
import io.ebean.migration.MigrationException;
45

56
import java.sql.*;
67
import java.util.ArrayList;
@@ -11,6 +12,7 @@
1112
/**
1213
* Handle database platform specific locking on db migration table.
1314
*/
15+
@SuppressWarnings({"SqlDialectInspection", "SqlSourceToSinkFlow"})
1416
class MigrationPlatform {
1517

1618
private static final System.Logger log = MigrationTable.log;
@@ -30,7 +32,7 @@ DdlDetect ddlDetect() {
3032
return DdlDetect.NONE;
3133
}
3234

33-
void unlockMigrationTable(String sqlTable, Connection connection) throws SQLException {
35+
void unlockMigrationTable(String sqlTable, Connection connection) {
3436
// do nothing by default for select for update case
3537
}
3638

@@ -118,9 +120,14 @@ void lockMigrationTable(String sqlTable, Connection connection) throws SQLExcept
118120
}
119121

120122
@Override
121-
void unlockMigrationTable(String sqlTable, Connection connection) throws SQLException {
122-
releaseLogicalLock(sqlTable, connection);
123-
connection.commit();
123+
void unlockMigrationTable(String sqlTable, Connection connection) {
124+
try {
125+
releaseLogicalLock(sqlTable, connection);
126+
connection.commit();
127+
} catch (SQLException e) {
128+
MigrationEngine.rollback(connection);
129+
throw new MigrationException("Error releasing logical lock for ebean migrations");
130+
}
124131
}
125132

126133
private boolean obtainLogicalLock(String sqlTable, Connection connection) throws SQLException {
@@ -191,10 +198,14 @@ private boolean obtainNamedLock(Connection connection) throws SQLException {
191198
}
192199

193200
@Override
194-
void unlockMigrationTable(String sqlTable, Connection connection) throws SQLException {
195-
String hash = Integer.toHexString(connection.getMetaData().getURL().hashCode());
196-
try (Statement query = connection.createStatement()) {
197-
query.execute("select release_lock('ebean_migration-" + hash + "')");
201+
void unlockMigrationTable(String sqlTable, Connection connection) {
202+
try {
203+
String hash = Integer.toHexString(connection.getMetaData().getURL().hashCode());
204+
try (Statement query = connection.createStatement()) {
205+
query.execute("select release_lock('ebean_migration-" + hash + "')");
206+
}
207+
} catch (SQLException e) {
208+
throw new MigrationException("Error releasing lock for ebean_migration", e);
198209
}
199210
}
200211
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ private void obtainLockWithWait() throws SQLException {
176176
/**
177177
* Release a lock on the migration table (MySql, MariaDB only).
178178
*/
179-
void unlockMigrationTable() throws SQLException {
179+
void unlockMigrationTable() {
180180
platform.unlockMigrationTable(sqlTable, connection);
181181
}
182182

ebean-migration/src/test/java/io/ebean/migration/MigrationRunnerTest.java

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,37 @@ public void run_when_fileSystemResources() {
5656
runner.run();
5757
}
5858

59+
@Test
60+
public void run_when_error() throws SQLException {
61+
62+
DataSourceConfig dataSourceConfig = new DataSourceConfig();
63+
dataSourceConfig.setDriver("org.h2.Driver");
64+
dataSourceConfig.setUrl("jdbc:h2:mem:err.db");
65+
dataSourceConfig.setUsername("sa");
66+
dataSourceConfig.setPassword("");
67+
68+
DataSourcePool dataSource = DataSourceFactory.create("test", dataSourceConfig);
69+
70+
MigrationConfig config = createMigrationConfig();
71+
config.setMigrationPath("dbmig_error");
72+
MigrationRunner runner = new MigrationRunner(config);
73+
try {
74+
runner.run(dataSource);
75+
} catch (Exception expected) {
76+
try (Connection connection = dataSource.getConnection()) {
77+
try (var pstmt = connection.prepareStatement("select count(*) from m1")) {
78+
try (var resultSet = pstmt.executeQuery()) {
79+
assertThat(resultSet.next()).isTrue();
80+
int val = resultSet.getInt(1);
81+
assertThat(val).isEqualTo(0);
82+
}
83+
} catch (SQLException ex) {
84+
fail(ex);
85+
}
86+
}
87+
}
88+
}
89+
5990
@Test
6091
public void run_when_suppliedConnection() {
6192

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
create table m1 (id integer, acol varchar(20));
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
insert into m1 (id, acol) values (1,'hi');
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
not valid sql

0 commit comments

Comments
 (0)