Skip to content

Commit e574b5c

Browse files
authored
Merge pull request #118 from FOCONIS/sqlserver-startup-issue
Fix flaky SqlServer startup issue
2 parents e4444ec + ebf217c commit e574b5c

File tree

2 files changed

+93
-0
lines changed

2 files changed

+93
-0
lines changed

src/main/java/io/ebean/test/containers/SqlServerContainer.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,27 @@ void dropCreateDatabase() {
8080
createRoleAndDatabase(true);
8181
}
8282

83+
@Override
84+
boolean checkConnectivity(boolean useAdmin) {
85+
if (!isDefaultCollation()) {
86+
if (!logsContain("The default collation was successfully changed", "Attempting to change default collation")) {
87+
// When starting a container other than the default collation, sql server rebuilds some indices for master/tempdb/...
88+
// if these indices are accessed during this period (e.g. 'drop database test_db' will access systable indices)
89+
// you will get a "Cannot resolve the collation conflict between" error or some other errors during startup
90+
return false;
91+
}
92+
}
93+
return super.checkConnectivity(useAdmin);
94+
}
95+
96+
private boolean isDefaultCollation() {
97+
if (dbConfig.getCollation() == null) {
98+
return false;
99+
}
100+
return "default".equals(dbConfig.getCollation())
101+
|| "SQL_Latin1_General_CP1_CI_AS".equals(dbConfig.getCollation());
102+
}
103+
83104
private void createRoleAndDatabase(boolean withDrop) {
84105
IllegalStateException cause = null;
85106
for (int i = 0; i < 3; i++) {
@@ -151,6 +172,8 @@ private void dropDatabaseIfExists(Connection connection) {
151172
}
152173

153174
private void dropDatabase(Connection connection, String dbName) {
175+
sqlRun(connection, "alter database " + dbName + " set offline with rollback immediate"); // close all connections to DB
176+
sqlRun(connection, "alter database " + dbName + " set online");
154177
sqlRun(connection, "drop database " + dbName);
155178
}
156179

src/test/java/io/ebean/test/containers/SqlServerContainerTest.java

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@
44
import org.junit.jupiter.api.Test;
55

66
import java.sql.Connection;
7+
import java.sql.DriverManager;
78
import java.sql.PreparedStatement;
89
import java.sql.SQLException;
10+
import java.sql.Statement;
911
import java.util.Properties;
1012

1113
class SqlServerContainerTest {
@@ -27,6 +29,27 @@ void start() {
2729
container.stopRemove();
2830
}
2931

32+
/**
33+
* startWithDropCreate does not work, when the container is running and there is a connection open.
34+
*/
35+
@Disabled
36+
@Test
37+
void start_dropCreate_withLeakingConnection() throws Exception {
38+
SqlServerContainer container = SqlServerContainer.builder(SQLSERVER_VER)
39+
.containerName("temp_sqlserver")
40+
.port(2433)
41+
.collation("Latin1_General_100_BIN2")
42+
.build();
43+
container.startWithDropCreate();
44+
try (Connection conn = DriverManager.getConnection(container.jdbcUrl(), container.dbConfig.getUsername(), container.dbConfig.getPassword())) {
45+
// drop during open connection
46+
container.startWithDropCreate();
47+
} finally {
48+
container.stopRemove();
49+
50+
}
51+
}
52+
3053
@Disabled
3154
@Test
3255
void start_when_defaultCollation() {
@@ -40,6 +63,19 @@ void start_when_defaultCollation() {
4063
container.stopRemove();
4164
}
4265

66+
@Disabled
67+
@Test
68+
void start_when_explicitDefaultCollation() {
69+
SqlServerContainer container = SqlServerContainer.builder(SQLSERVER_VER)
70+
.containerName("temp_sqlserver")
71+
.port(2433)
72+
.collation("SQL_Latin1_General_CP1_CI_AS")
73+
.build();
74+
75+
container.startWithCreate();
76+
container.stopRemove();
77+
}
78+
4379
@Disabled
4480
@Test
4581
void start_when_noCollation() {
@@ -65,6 +101,40 @@ void start_when_explicitCollation() {
65101
container.stopRemove();
66102
}
67103

104+
/**
105+
* When Collation is changed, the server sometimes will not start with dropCreate, because there are async
106+
* processes on container start. We must ensure, that we wait until container is started.
107+
* <p>
108+
* It is very hard to reproduce, I could only reproduce it with "startWithDropCreate" and an active
109+
* screen sharing session... So the test may look a bit messy to get the correct timing.
110+
* <p>
111+
* I observed the output:
112+
* 16:14:42.726 [main] DEBUG io.ebean.test.containers - connectivity confirmed for temp_sqlserver
113+
* <p>
114+
* comparing the timestamp with the docker-logs, the server was in the middle of collation change process.
115+
*/
116+
@Disabled
117+
@Test
118+
void start_dropCreate_explicitCollationTimmingTest() throws Exception {
119+
SqlServerContainer container = SqlServerContainer.builder(SQLSERVER_VER)
120+
.containerName("temp_sqlserver")
121+
.port(2433)
122+
.collation("Latin1_General_100_BIN2")
123+
.build();
124+
for (int i = 0; i < 10; i++) {
125+
container.startWithDropCreate();
126+
try (Connection conn = DriverManager.getConnection(container.jdbcUrl(), container.dbConfig.getUsername(), container.dbConfig.getPassword())) {
127+
for (int j = 0; j < 10; j++) {
128+
Statement stmt = conn.createStatement();
129+
stmt.execute("create table my_test" + j + " (id int)");
130+
conn.commit();
131+
}
132+
Thread.sleep(2000);
133+
container.stopRemove();
134+
}
135+
}
136+
}
137+
68138
@Disabled
69139
@Test
70140
void viaContainerFactory() {

0 commit comments

Comments
 (0)