Skip to content

Commit 0dcfda3

Browse files
authored
Merge pull request #1054 from the-thing/jdbc-test
New `jcstress` concurrency stress testing module
2 parents 4fea4b6 + 25c7580 commit 0dcfda3

File tree

9 files changed

+1179
-44
lines changed

9 files changed

+1179
-44
lines changed

.github/workflows/nightly-build.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ jobs:
5858
org.quickfixj.quickfixj-orchestration
5959
org.quickfixj.quickfixj-parent
6060
org.quickfixj.quickfixj-perf-test
61+
org.quickfixj.quickfixj-stress-test
6162
6263
- name: Deploy SNAPSHOT
6364
run: ./mvnw deploy -pl '!quickfixj-distribution' -B -V -Dmaven.install.skip=true -DskipTests -D"java.util.logging.config.file"="${{github.workspace}}/quickfixj-core/src/test/resources/logging.properties" -D"http.keepAlive"="false" -D"maven.wagon.http.pool"="false" -D"maven.wagon.httpconnectionManager.ttlSeconds"="120"

pom.xml

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,8 @@
5656
<module>quickfixj-all</module>
5757
<module>quickfixj-distribution</module>
5858
<module>quickfixj-perf-test</module>
59-
</modules>
59+
<module>quickfixj-stress-test</module>
60+
</modules>
6061

6162
<properties>
6263
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
@@ -69,7 +70,7 @@
6970
<maven.compiler.source>8</maven.compiler.source>
7071
<maven.compiler.target>8</maven.compiler.target>
7172
<mainClass/>
72-
<maven.version>3.9.11</maven.version> <!-- please also change version in .mvn/wrapper/maven-wrapper.properties -->
73+
<maven.version>3.9.11</maven.version> <!-- please also change version in .mvn/wrapper/maven-wrapper.properties -->
7374
<maven-libs-version>${maven.version}</maven-libs-version>
7475
<maven-plugin-api-version>${maven.version}</maven-plugin-api-version>
7576
<maven-resources-plugin-version>3.4.0</maven-resources-plugin-version>
@@ -102,7 +103,9 @@
102103
<orchestra.file>OrchestraFIXLatest.xml</orchestra.file>
103104
<org.quickfixj.orchestra.tools.version>1.0.3</org.quickfixj.orchestra.tools.version>
104105
<jaxen.version>2.0.0</jaxen.version>
105-
<jmh.version>1.37</jmh.version>
106+
<jmh.version>1.37</jmh.version>
107+
<jcstress.version>0.16</jcstress.version>
108+
<hsqldb.version>1.8.0.10</hsqldb.version>
106109
</properties>
107110

108111
<dependencyManagement>
@@ -273,7 +276,7 @@
273276
<version>${maven-compiler-plugin-version}</version>
274277
<inherited>true</inherited>
275278
<configuration>
276-
<forceJavacCompilerUse>true</forceJavacCompilerUse> <!-- https://bugs.openjdk.java.net/browse/JDK-8216202 -->
279+
<forceLegacyJavacApi>true</forceLegacyJavacApi> <!-- https://bugs.openjdk.java.net/browse/JDK-8216202 -->
277280
<meminitial>2g</meminitial>
278281
<maxmem>4g</maxmem>
279282
</configuration>

quickfixj-core/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@
102102
<dependency>
103103
<groupId>hsqldb</groupId>
104104
<artifactId>hsqldb</artifactId>
105-
<version>1.8.0.10</version>
105+
<version>${hsqldb.version}</version>
106106
<scope>test</scope>
107107
</dependency>
108108
<dependency>

quickfixj-core/src/main/java/quickfix/JdbcStore.java

Lines changed: 55 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -54,19 +54,9 @@ class JdbcStore implements MessageStore {
5454

5555
public JdbcStore(SessionSettings settings, SessionID sessionID, DataSource ds) throws Exception {
5656
this.sessionID = sessionID;
57-
if (settings.isSetting(sessionID, SETTING_JDBC_STORE_SESSIONS_TABLE_NAME)) {
58-
sessionTableName = settings
59-
.getString(sessionID, SETTING_JDBC_STORE_SESSIONS_TABLE_NAME);
60-
} else {
61-
sessionTableName = DEFAULT_SESSION_TABLE_NAME;
62-
}
6357

64-
if (settings.isSetting(sessionID, SETTING_JDBC_STORE_MESSAGES_TABLE_NAME)) {
65-
messageTableName = settings
66-
.getString(sessionID, SETTING_JDBC_STORE_MESSAGES_TABLE_NAME);
67-
} else {
68-
messageTableName = DEFAULT_MESSAGE_TABLE_NAME;
69-
}
58+
sessionTableName = getSessionTableName(settings, sessionID);
59+
messageTableName = getMessageTableName(settings, sessionID);
7060

7161
if (settings.isSetting(sessionID, SETTING_JDBC_SESSION_ID_DEFAULT_PROPERTY_VALUE)) {
7262
defaultSessionIdPropertyValue = settings.getString(sessionID,
@@ -90,34 +80,67 @@ public JdbcStore(SessionSettings settings, SessionID sessionID, DataSource ds) t
9080
loadCache();
9181
}
9282

93-
private void setSqlStrings() {
94-
String idWhereClause = JdbcUtil.getIDWhereClause(extendedSessionIdSupported);
95-
String idColumns = JdbcUtil.getIDColumns(extendedSessionIdSupported);
96-
String idPlaceholders = JdbcUtil.getIDPlaceholders(extendedSessionIdSupported);
83+
public static String getSessionTableName(SessionSettings settings, SessionID sessionID) throws ConfigError {
84+
if (settings.isSetting(sessionID, SETTING_JDBC_STORE_SESSIONS_TABLE_NAME)) {
85+
return settings.getString(sessionID, SETTING_JDBC_STORE_SESSIONS_TABLE_NAME);
86+
} else {
87+
return DEFAULT_SESSION_TABLE_NAME;
88+
}
89+
}
9790

98-
SQL_UPDATE_SEQNUMS = "UPDATE " + sessionTableName + " SET incoming_seqnum=?, "
99-
+ "outgoing_seqnum=? WHERE " + idWhereClause;
91+
public static String getMessageTableName(SessionSettings settings, SessionID sessionID) throws ConfigError {
92+
if (settings.isSetting(sessionID, SETTING_JDBC_STORE_MESSAGES_TABLE_NAME)) {
93+
return settings.getString(sessionID, SETTING_JDBC_STORE_MESSAGES_TABLE_NAME);
94+
} else {
95+
return DEFAULT_MESSAGE_TABLE_NAME;
96+
}
97+
}
10098

101-
SQL_INSERT_SESSION = "INSERT INTO " + sessionTableName + " (" + idColumns
102-
+ ", creation_time,incoming_seqnum, outgoing_seqnum) VALUES (" + idPlaceholders
103-
+ ",?,?,?)";
99+
public static String getUpdateSequenceNumsSql(String sessionTableName, String idWhereClause) {
100+
return "UPDATE " + sessionTableName + " SET incoming_seqnum=?, " + "outgoing_seqnum=? WHERE " + idWhereClause;
101+
}
104102

105-
SQL_GET_SEQNUMS = "SELECT creation_time, incoming_seqnum, outgoing_seqnum FROM "
106-
+ sessionTableName + " WHERE " + idWhereClause;
103+
public static String getInsertSessionSql(String sessionTableName, String idColumns, String idPlaceholders) {
104+
return "INSERT INTO " + sessionTableName + " (" + idColumns + ", creation_time,incoming_seqnum, outgoing_seqnum) VALUES (" + idPlaceholders + ",?,?,?)";
105+
}
107106

108-
SQL_UPDATE_MESSAGE = "UPDATE " + messageTableName + " SET message=? " + "WHERE "
109-
+ idWhereClause + " and msgseqnum=?";
107+
public static String getSequenceNumsSql(String sessionTableName, String idWhereClause) {
108+
return "SELECT creation_time, incoming_seqnum, outgoing_seqnum FROM " + sessionTableName + " WHERE " + idWhereClause;
109+
}
110110

111-
SQL_INSERT_MESSAGE = "INSERT INTO " + messageTableName + " (" + idColumns
112-
+ ", msgseqnum,message) VALUES (" + idPlaceholders + ",?,?)";
111+
public static String getUpdateMessageSql(String messageTableName, String idWhereClause) {
112+
return "UPDATE " + messageTableName + " SET message=? " + "WHERE " + idWhereClause + " and msgseqnum=?";
113+
}
113114

114-
SQL_GET_MESSAGES = "SELECT message FROM " + messageTableName + " WHERE " + idWhereClause
115-
+ " and msgseqnum>=? and msgseqnum<=? " + "ORDER BY msgseqnum";
115+
public static String getInsertMessageSql(String messageTableName, String idColumns, String idPlaceholders) {
116+
return "INSERT INTO " + messageTableName + " (" + idColumns + ", msgseqnum,message) VALUES (" + idPlaceholders + ",?,?)";
117+
}
116118

117-
SQL_UPDATE_SESSION = "UPDATE " + sessionTableName + " SET creation_time=?, "
118-
+ "incoming_seqnum=?, outgoing_seqnum=? " + "WHERE " + idWhereClause;
119+
public static String getMessagesSql(String messageTableName, String idWhereClause) {
120+
return "SELECT message FROM " + messageTableName + " WHERE " + idWhereClause + " and msgseqnum>=? and msgseqnum<=? " + "ORDER BY msgseqnum";
121+
}
122+
123+
public static String getUpdateSessionSql(String sessionTableName, String idWhereClause) {
124+
return "UPDATE " + sessionTableName + " SET creation_time=?, " + "incoming_seqnum=?, outgoing_seqnum=? " + "WHERE " + idWhereClause;
125+
}
119126

120-
SQL_DELETE_MESSAGES = "DELETE FROM " + messageTableName + " WHERE " + idWhereClause;
127+
public static String getDeleteMessagesSql(String messageTableName, String idWhereClause) {
128+
return "DELETE FROM " + messageTableName + " WHERE " + idWhereClause;
129+
}
130+
131+
private void setSqlStrings() {
132+
String idWhereClause = JdbcUtil.getIDWhereClause(extendedSessionIdSupported);
133+
String idColumns = JdbcUtil.getIDColumns(extendedSessionIdSupported);
134+
String idPlaceholders = JdbcUtil.getIDPlaceholders(extendedSessionIdSupported);
135+
136+
SQL_UPDATE_SEQNUMS = getUpdateSequenceNumsSql(sessionTableName, idWhereClause);
137+
SQL_INSERT_SESSION = getInsertSessionSql(sessionTableName, idColumns, idPlaceholders);
138+
SQL_GET_SEQNUMS = getSequenceNumsSql(sessionTableName, idWhereClause);
139+
SQL_UPDATE_MESSAGE = getUpdateMessageSql(messageTableName, idWhereClause);
140+
SQL_INSERT_MESSAGE = getInsertMessageSql(messageTableName, idColumns, idPlaceholders);
141+
SQL_GET_MESSAGES = getMessagesSql(messageTableName, idWhereClause);
142+
SQL_UPDATE_SESSION = getUpdateSessionSql(sessionTableName, idWhereClause);
143+
SQL_DELETE_MESSAGES = getDeleteMessagesSql(messageTableName, idWhereClause);
121144
}
122145

123146
private void loadCache() throws SQLException, IOException {
@@ -238,7 +261,6 @@ public void get(int startSequence, int endSequence, Collection<String> messages)
238261
public boolean set(int sequence, String message) throws IOException {
239262
Connection connection = null;
240263
PreparedStatement insert = null;
241-
ResultSet rs = null;
242264
try {
243265
connection = dataSource.getConnection();
244266
insert = connection.prepareStatement(SQL_INSERT_MESSAGE);
@@ -263,7 +285,6 @@ public boolean set(int sequence, String message) throws IOException {
263285
}
264286
}
265287
} finally {
266-
JdbcUtil.close(sessionID, rs);
267288
JdbcUtil.close(sessionID, insert);
268289
JdbcUtil.close(sessionID, connection);
269290
}

quickfixj-distribution/pom.xml

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -122,11 +122,16 @@
122122
<artifactId>quickfixj-messages-fix40</artifactId>
123123
<version>${project.version}</version>
124124
</dependency>
125-
<dependency>
126-
<groupId>org.quickfixj</groupId>
127-
<artifactId>quickfixj-perf-test</artifactId>
128-
<version>${project.version}</version>
129-
</dependency>
125+
<dependency>
126+
<groupId>org.quickfixj</groupId>
127+
<artifactId>quickfixj-perf-test</artifactId>
128+
<version>${project.version}</version>
129+
</dependency>
130+
<dependency>
131+
<groupId>org.quickfixj</groupId>
132+
<artifactId>quickfixj-stress-test</artifactId>
133+
<version>${project.version}</version>
134+
</dependency>
130135
<dependency>
131136
<groupId>com.sleepycat</groupId>
132137
<artifactId>je</artifactId>

quickfixj-stress-test/README.md

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
# QuickFIX/J Java Concurrency Stress Test
2+
3+
This is a [Java Concurrency Stress (jcstress)](https://github.com/openjdk/jcstress) concurrency stress testing module for QuickFIX/J FIX protocol implementation.
4+
5+
## How to Run
6+
7+
Concurrency stress testing classes can be individually run using your favorite IDE or command line.
8+
9+
### Building the Executable JAR
10+
11+
#### Build Full Project
12+
13+
To build the entire project including all modules:
14+
15+
```bash
16+
$ mvn clean package
17+
```
18+
19+
#### Build Only `quickfixj-stress-test` Module
20+
21+
Build the stress test module with required dependencies, skipping test execution:
22+
23+
```bash
24+
$ mvn clean package -pl quickfixj-stress-test -am -Dmaven.javadoc.skip=true -DskipTests -DskipAT=true -PskipBundlePlugin,minimal-fix-latest
25+
```
26+
27+
**Command Options Explained:**
28+
- `-pl quickfixj-stress-test` - Build only the stress test module
29+
- `-am` - Also build required dependent modules
30+
- `-Dmaven.javadoc.skip` - Skip JavaDoc generation
31+
- `-DskipTests` - Skip running unit test cases during build
32+
- `-DskipAT=true` - Skip running acceptance tests
33+
- `-PskipBundlePlugin,minimal-fix-latest` - Skip bundle plugin and use minimal FIX Orchestra code generation
34+
35+
### Running Tests
36+
37+
#### Run Complete Test Suite
38+
39+
Execute all concurrency regression test cases:
40+
41+
```bash
42+
$ java -jar quickfixj-stress-test/target/quickfixj-stress-test.jar
43+
```
44+
45+
#### Run a Single Test
46+
47+
Run a specific test class or test group (if test cases are nested):
48+
49+
```bash
50+
$ java -jar quickfixj-stress-test/target/quickfixj-stress-test.jar -t JdbcStoreStressTest
51+
```
52+
53+
#### View Available Options
54+
55+
Display all available command-line options:
56+
57+
```bash
58+
$ java -jar quickfixj-stress-test/target/quickfixj-stress-test.jar -h
59+
```
60+
61+
## Common Options
62+
63+
Some useful jcstress options include:
64+
65+
- `h` - Display help
66+
- `-t <test>` - Run specific test or test group
67+
- `-v` - Verbose mode
68+
- `-jvmArgs <args>` - Additional JVM arguments
69+
70+
## Additional Notes
71+
72+
- For CI/CD integration, use the full path to the JAR file
73+
- Stress tests may take significant time to complete depending on your system resources
74+
- Review jcstress documentation for advanced configuration options
75+
76+
## Troubleshooting
77+
78+
If you encounter issues:
79+
80+
- Verify the JAR file was built successfully in `quickfixj-stress-test/target/`
81+
- Check that you have sufficient system resources (CPU, memory)
82+
- Review the jcstress output for specific error messages

quickfixj-stress-test/pom.xml

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
5+
<modelVersion>4.0.0</modelVersion>
6+
7+
<parent>
8+
<groupId>org.quickfixj</groupId>
9+
<artifactId>quickfixj-parent</artifactId>
10+
<version>3.0.0-SNAPSHOT</version>
11+
</parent>
12+
13+
<artifactId>quickfixj-stress-test</artifactId>
14+
<packaging>jar</packaging>
15+
16+
<name>QuickFIX/J Stress test</name>
17+
<description>QuickFIX/J Stress test</description>
18+
<url>http://www.quickfixj.org</url>
19+
20+
<dependencies>
21+
<dependency>
22+
<groupId>org.quickfixj</groupId>
23+
<artifactId>quickfixj-core</artifactId>
24+
<version>${project.version}</version>
25+
</dependency>
26+
<dependency>
27+
<groupId>org.openjdk.jcstress</groupId>
28+
<artifactId>jcstress-core</artifactId>
29+
<version>${jcstress.version}</version>
30+
</dependency>
31+
<dependency>
32+
<groupId>org.slf4j</groupId>
33+
<artifactId>slf4j-jdk14</artifactId>
34+
<version>${slf4j.version}</version>
35+
</dependency>
36+
<dependency>
37+
<groupId>org.mockito</groupId>
38+
<artifactId>mockito-core</artifactId>
39+
<scope>compile</scope>
40+
</dependency>
41+
</dependencies>
42+
43+
<build>
44+
<plugins>
45+
<plugin>
46+
<groupId>org.apache.maven.plugins</groupId>
47+
<artifactId>maven-shade-plugin</artifactId>
48+
<executions>
49+
<execution>
50+
<id>create-jcstress-jar</id>
51+
<phase>package</phase>
52+
<goals>
53+
<goal>shade</goal>
54+
</goals>
55+
<configuration>
56+
<finalName>${project.artifactId}</finalName>
57+
<transformers>
58+
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
59+
<mainClass>org.openjdk.jcstress.Main</mainClass>
60+
</transformer>
61+
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
62+
<resource>META-INF/TestList</resource>
63+
</transformer>
64+
</transformers>
65+
<createDependencyReducedPom>false</createDependencyReducedPom>
66+
<shadedArtifactAttached>true</shadedArtifactAttached>
67+
<shadedClassifierName>standalone</shadedClassifierName>
68+
</configuration>
69+
</execution>
70+
</executions>
71+
</plugin>
72+
</plugins>
73+
</build>
74+
</project>

0 commit comments

Comments
 (0)